1 | import is from '@sindresorhus/is';
|
2 | import { Predicate } from './predicate.js';
|
3 | export class StringPredicate extends Predicate {
|
4 | |
5 |
|
6 |
|
7 | constructor(options) {
|
8 | super('string', options);
|
9 | }
|
10 | |
11 |
|
12 |
|
13 |
|
14 |
|
15 | length(length) {
|
16 | return this.addValidator({
|
17 | message: (value, label) => `Expected ${label} to have length \`${length}\`, got \`${value}\``,
|
18 | validator: value => value.length === length,
|
19 | });
|
20 | }
|
21 | |
22 |
|
23 |
|
24 |
|
25 |
|
26 | minLength(length) {
|
27 | return this.addValidator({
|
28 | message: (value, label) => `Expected ${label} to have a minimum length of \`${length}\`, got \`${value}\``,
|
29 | validator: value => value.length >= length,
|
30 | negatedMessage: (value, label) => `Expected ${label} to have a maximum length of \`${length - 1}\`, got \`${value}\``,
|
31 | });
|
32 | }
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 | maxLength(length) {
|
39 | return this.addValidator({
|
40 | message: (value, label) => `Expected ${label} to have a maximum length of \`${length}\`, got \`${value}\``,
|
41 | validator: value => value.length <= length,
|
42 | negatedMessage: (value, label) => `Expected ${label} to have a minimum length of \`${length + 1}\`, got \`${value}\``,
|
43 | });
|
44 | }
|
45 | |
46 |
|
47 |
|
48 |
|
49 |
|
50 | matches(regex) {
|
51 | return this.addValidator({
|
52 | message: (value, label) => `Expected ${label} to match \`${regex}\`, got \`${value}\``,
|
53 | validator: value => regex.test(value),
|
54 | });
|
55 | }
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 | startsWith(searchString) {
|
62 | return this.addValidator({
|
63 | message: (value, label) => `Expected ${label} to start with \`${searchString}\`, got \`${value}\``,
|
64 | validator: value => value.startsWith(searchString),
|
65 | });
|
66 | }
|
67 | |
68 |
|
69 |
|
70 |
|
71 |
|
72 | endsWith(searchString) {
|
73 | return this.addValidator({
|
74 | message: (value, label) => `Expected ${label} to end with \`${searchString}\`, got \`${value}\``,
|
75 | validator: value => value.endsWith(searchString),
|
76 | });
|
77 | }
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 | includes(searchString) {
|
84 | return this.addValidator({
|
85 | message: (value, label) => `Expected ${label} to include \`${searchString}\`, got \`${value}\``,
|
86 | validator: value => value.includes(searchString),
|
87 | });
|
88 | }
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 | oneOf(list) {
|
95 | return this.addValidator({
|
96 | message(value, label) {
|
97 | let printedList = JSON.stringify(list);
|
98 | if (list.length > 10) {
|
99 | const overflow = list.length - 10;
|
100 | printedList = JSON.stringify(list.slice(0, 10)).replace(/]$/, `,…+${overflow} more]`);
|
101 | }
|
102 | return `Expected ${label} to be one of \`${printedList}\`, got \`${value}\``;
|
103 | },
|
104 | validator: value => list.includes(value),
|
105 | });
|
106 | }
|
107 | |
108 |
|
109 |
|
110 | get empty() {
|
111 | return this.addValidator({
|
112 | message: (value, label) => `Expected ${label} to be empty, got \`${value}\``,
|
113 | validator: value => value === '',
|
114 | });
|
115 | }
|
116 | |
117 |
|
118 |
|
119 | get nonBlank() {
|
120 | return this.addValidator({
|
121 | message(value, label) {
|
122 |
|
123 |
|
124 | const madeVisible = value
|
125 | .replaceAll(' ', '·')
|
126 | .replaceAll('\f', '\\f')
|
127 | .replaceAll('\n', '\\n')
|
128 | .replaceAll('\r', '\\r')
|
129 | .replaceAll('\t', '\\t')
|
130 | .replaceAll('\v', '\\v');
|
131 | return `Expected ${label} to not be only whitespace, got \`${madeVisible}\``;
|
132 | },
|
133 | validator: value => value.trim() !== '',
|
134 | });
|
135 | }
|
136 | |
137 |
|
138 |
|
139 | get nonEmpty() {
|
140 | return this.addValidator({
|
141 | message: (_, label) => `Expected ${label} to not be empty`,
|
142 | validator: value => value !== '',
|
143 | });
|
144 | }
|
145 | |
146 |
|
147 |
|
148 |
|
149 |
|
150 | equals(expected) {
|
151 | return this.addValidator({
|
152 | message: (value, label) => `Expected ${label} to be equal to \`${expected}\`, got \`${value}\``,
|
153 | validator: value => value === expected,
|
154 | });
|
155 | }
|
156 | |
157 |
|
158 |
|
159 | get alphanumeric() {
|
160 | return this.addValidator({
|
161 | message: (value, label) => `Expected ${label} to be alphanumeric, got \`${value}\``,
|
162 | validator: value => /^[a-z\d]+$/i.test(value),
|
163 | });
|
164 | }
|
165 | |
166 |
|
167 |
|
168 | get alphabetical() {
|
169 | return this.addValidator({
|
170 | message: (value, label) => `Expected ${label} to be alphabetical, got \`${value}\``,
|
171 | validator: value => /^[a-z]+$/gi.test(value),
|
172 | });
|
173 | }
|
174 | |
175 |
|
176 |
|
177 | get numeric() {
|
178 | return this.addValidator({
|
179 | message: (value, label) => `Expected ${label} to be numeric, got \`${value}\``,
|
180 | validator: value => /^[+-]?\d+$/i.test(value),
|
181 | });
|
182 | }
|
183 | |
184 |
|
185 |
|
186 | get date() {
|
187 | return this.addValidator({
|
188 | message: (value, label) => `Expected ${label} to be a date, got \`${value}\``,
|
189 | validator: value => is.validDate(new Date(value)),
|
190 | });
|
191 | }
|
192 | |
193 |
|
194 |
|
195 | get lowercase() {
|
196 | return this.addValidator({
|
197 | message: (value, label) => `Expected ${label} to be lowercase, got \`${value}\``,
|
198 | validator: value => value.trim() !== '' && value === value.toLowerCase(),
|
199 | });
|
200 | }
|
201 | |
202 |
|
203 |
|
204 | get uppercase() {
|
205 | return this.addValidator({
|
206 | message: (value, label) => `Expected ${label} to be uppercase, got \`${value}\``,
|
207 | validator: value => value.trim() !== '' && value === value.toUpperCase(),
|
208 | });
|
209 | }
|
210 | |
211 |
|
212 |
|
213 | get url() {
|
214 | return this.addValidator({
|
215 | message: (value, label) => `Expected ${label} to be a URL, got \`${value}\``,
|
216 | validator: is.urlString,
|
217 | });
|
218 | }
|
219 | }
|