UNPKG

6.83 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Predicate = exports.validatorSymbol = void 0;
4const is_1 = require("@sindresorhus/is");
5const argument_error_1 = require("../argument-error");
6const not_1 = require("../operators/not");
7const base_predicate_1 = require("./base-predicate");
8const generate_argument_error_message_1 = require("../utils/generate-argument-error-message");
9/**
10@hidden
11*/
12exports.validatorSymbol = Symbol('validators');
13/**
14@hidden
15*/
16class Predicate {
17 constructor(type, options = {}) {
18 Object.defineProperty(this, "type", {
19 enumerable: true,
20 configurable: true,
21 writable: true,
22 value: type
23 });
24 Object.defineProperty(this, "options", {
25 enumerable: true,
26 configurable: true,
27 writable: true,
28 value: options
29 });
30 Object.defineProperty(this, "context", {
31 enumerable: true,
32 configurable: true,
33 writable: true,
34 value: {
35 validators: []
36 }
37 });
38 this.context = {
39 ...this.context,
40 ...this.options
41 };
42 const typeString = this.type.charAt(0).toLowerCase() + this.type.slice(1);
43 this.addValidator({
44 message: (value, label) => {
45 // We do not include type in this label as we do for other messages, because it would be redundant.
46 const label_ = label === null || label === void 0 ? void 0 : label.slice(this.type.length + 1);
47 // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
48 return `Expected ${label_ || 'argument'} to be of type \`${this.type}\` but received type \`${is_1.default(value)}\``;
49 },
50 validator: value => is_1.default[typeString](value)
51 });
52 }
53 /**
54 @hidden
55 */
56 [base_predicate_1.testSymbol](value, main, label, idLabel) {
57 // Create a map of labels -> received errors.
58 const errors = new Map();
59 for (const { validator, message } of this.context.validators) {
60 if (this.options.optional === true && value === undefined) {
61 continue;
62 }
63 let result;
64 try {
65 result = validator(value);
66 }
67 catch (error) {
68 // Any errors caught means validators couldn't process the input.
69 result = error;
70 }
71 if (result === true) {
72 continue;
73 }
74 const label2 = is_1.default.function_(label) ? label() : label;
75 const labelWithTick = (label2 && idLabel) ? `\`${label2}\`` : label2;
76 const label_ = labelWithTick ?
77 `${this.type} ${labelWithTick}` :
78 this.type;
79 const mapKey = label2 || this.type;
80 // Get the current errors encountered for this label.
81 const currentErrors = errors.get(mapKey);
82 // Pre-generate the error message that will be reported to the user.
83 const errorMessage = message(value, label_, result);
84 // If we already have any errors for this label.
85 if (currentErrors) {
86 // If we don't already have this error logged, add it.
87 currentErrors.add(errorMessage);
88 }
89 else {
90 // Set this label and error in the full map.
91 errors.set(mapKey, new Set([errorMessage]));
92 }
93 }
94 // If we have any errors to report, throw.
95 if (errors.size > 0) {
96 // Generate the `error.message` property.
97 const message = generate_argument_error_message_1.generateArgumentErrorMessage(errors);
98 throw new argument_error_1.ArgumentError(message, main, errors);
99 }
100 }
101 /**
102 @hidden
103 */
104 get [exports.validatorSymbol]() {
105 return this.context.validators;
106 }
107 /**
108 Invert the following validators.
109 */
110 get not() {
111 return not_1.not(this);
112 }
113 /**
114 Test if the value matches a custom validation function. The validation function should return an object containing a `validator` and `message`. If the `validator` is `false`, the validation fails and the `message` will be used as error message. If the `message` is a function, the function is invoked with the `label` as argument to let you further customize the error message.
115
116 @param customValidator - Custom validation function.
117 */
118 validate(customValidator) {
119 return this.addValidator({
120 message: (_, label, error) => typeof error === 'string' ?
121 `(${label}) ${error}` :
122 error(label),
123 validator: value => {
124 const { message, validator } = customValidator(value);
125 if (validator) {
126 return true;
127 }
128 return message;
129 }
130 });
131 }
132 /**
133 Test if the value matches a custom validation function. The validation function should return `true` if the value passes the function. If the function either returns `false` or a string, the function fails and the string will be used as error message.
134
135 @param validator - Validation function.
136 */
137 is(validator) {
138 return this.addValidator({
139 message: (value, label, error) => (error ?
140 `(${label}) ${error}` :
141 `Expected ${label} \`${value}\` to pass custom validation function`),
142 validator
143 });
144 }
145 /**
146 Provide a new error message to be thrown when the validation fails.
147
148 @param newMessage - Either a string containing the new message or a function returning the new message.
149
150 @example
151 ```
152 ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow'));
153 //=> ArgumentError: Expected unicorn, got rainbow
154 ```
155
156 @example
157 ```
158 ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``));
159 //=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈`
160 ```
161 */
162 message(newMessage) {
163 const { validators } = this.context;
164 validators[validators.length - 1].message = (value, label) => {
165 if (typeof newMessage === 'function') {
166 return newMessage(value, label);
167 }
168 return newMessage;
169 };
170 return this;
171 }
172 /**
173 Register a new validator.
174
175 @param validator - Validator to register.
176 */
177 addValidator(validator) {
178 this.context.validators.push(validator);
179 return this;
180 }
181}
182exports.Predicate = Predicate;