@stackbit/cms-contentful 1.0.5 → 1.0.6-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contentful-schema-converter.d.ts +8 -3
- package/dist/contentful-schema-converter.d.ts.map +1 -1
- package/dist/contentful-schema-converter.js +159 -24
- package/dist/contentful-schema-converter.js.map +1 -1
- package/package.json +5 -5
- package/src/contentful-schema-converter.ts +215 -54
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { omitByNil } from '@stackbit/utils';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { ContentFields, ContentTypeProps, Control, EditorInterfaceProps } from 'contentful-management';
|
|
3
|
+
import * as StackbitTypes from '@stackbit/types';
|
|
4
|
+
import type { Field, FieldSpecificProps, RequiredBy } from '@stackbit/types';
|
|
5
|
+
import type { FieldType, ContentFields, ContentTypeProps, Control, EditorInterfaceProps, ContentTypeFieldValidation } from 'contentful-management';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
CONTENTFUL_NODE_TYPES_MAP,
|
|
@@ -13,6 +13,13 @@ import {
|
|
|
13
13
|
} from './contentful-consts';
|
|
14
14
|
|
|
15
15
|
type ExtendedFieldType = FieldType | ResourceLink | ResourceLinkArray;
|
|
16
|
+
type ContentfulField = ContentFields & ExtendedFieldType;
|
|
17
|
+
|
|
18
|
+
declare module 'contentful-management' {
|
|
19
|
+
interface ContentTypeFieldValidation {
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
16
23
|
|
|
17
24
|
interface ResourceLink {
|
|
18
25
|
type: 'ResourceLink';
|
|
@@ -42,7 +49,7 @@ export interface ConvertSchemaOptions {
|
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
export function convertSchema({ contentTypes, editorInterfaces, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList }: ConvertSchemaOptions): {
|
|
45
|
-
models: Model[];
|
|
52
|
+
models: StackbitTypes.Model[];
|
|
46
53
|
} {
|
|
47
54
|
const editorInterfaceByContentTypeId = _.chain(editorInterfaces)
|
|
48
55
|
.filter({
|
|
@@ -81,7 +88,7 @@ interface MapModelOptions {
|
|
|
81
88
|
bynderImagesAsList: boolean;
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
function mapModel(options: MapModelOptions): Model {
|
|
91
|
+
function mapModel(options: MapModelOptions): StackbitTypes.Model {
|
|
85
92
|
const contentType = options.contentType;
|
|
86
93
|
const contentTypeId = contentType.sys.id;
|
|
87
94
|
const mappedFields = mapFields(options);
|
|
@@ -110,7 +117,7 @@ interface MapFieldsOptions {
|
|
|
110
117
|
|
|
111
118
|
function mapFields({ contentType, editorInterfaceByContentTypeId, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList }: MapFieldsOptions) {
|
|
112
119
|
const contentTypeId = contentType.sys.id;
|
|
113
|
-
const fields = contentType.fields;
|
|
120
|
+
const fields = contentType.fields as ContentfulField[];
|
|
114
121
|
const editorInterfaceControls = editorInterfaceByContentTypeId[contentTypeId]?.controls;
|
|
115
122
|
const editorInterfaceControlsByFieldId = _.keyBy(editorInterfaceControls, 'fieldId');
|
|
116
123
|
|
|
@@ -126,7 +133,7 @@ function mapFields({ contentType, editorInterfaceByContentTypeId, defaultLocaleC
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
interface MapFieldOptions {
|
|
129
|
-
field:
|
|
136
|
+
field: ContentfulField;
|
|
130
137
|
editorInterfaceControlsByFieldId: Record<string, Control>;
|
|
131
138
|
defaultLocaleCode: string;
|
|
132
139
|
cloudinaryImagesAsList: boolean;
|
|
@@ -139,10 +146,11 @@ function mapField({ field, editorInterfaceControlsByFieldId, defaultLocaleCode,
|
|
|
139
146
|
const isRequired = field.required;
|
|
140
147
|
const isReadonly = _.get(editorInterfaceControl, 'settings.readOnly');
|
|
141
148
|
const isHidden = field.disabled ?? isReadonly;
|
|
149
|
+
const validations = field.validations;
|
|
142
150
|
const localized = field.localized;
|
|
143
151
|
const defaultValue = getDefaultValue(field, defaultLocaleCode, editorInterfaceControl);
|
|
144
152
|
const defaultAsConst = isRequired && isReadonly && !_.isUndefined(defaultValue);
|
|
145
|
-
const
|
|
153
|
+
const fieldSpecificProps = convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList });
|
|
146
154
|
|
|
147
155
|
return _.assign(
|
|
148
156
|
{
|
|
@@ -157,13 +165,14 @@ function mapField({ field, editorInterfaceControlsByFieldId, defaultLocaleCode,
|
|
|
157
165
|
const: defaultAsConst ? defaultValue : undefined,
|
|
158
166
|
readOnly: isReadonly,
|
|
159
167
|
hidden: isHidden,
|
|
168
|
+
validations: convertValidations(validations),
|
|
160
169
|
localized: localized
|
|
161
170
|
}),
|
|
162
|
-
|
|
171
|
+
fieldSpecificProps
|
|
163
172
|
);
|
|
164
173
|
}
|
|
165
174
|
|
|
166
|
-
function getDefaultValue(field:
|
|
175
|
+
function getDefaultValue(field: ContentfulField, defaultLocaleCode: string, editorInterfaceControl?: Control) {
|
|
167
176
|
// Contentful defaultValue is an object with locale keys and default values per locale
|
|
168
177
|
const defaultValue = field.defaultValue;
|
|
169
178
|
if (typeof defaultValue !== 'undefined') {
|
|
@@ -180,14 +189,14 @@ function getDefaultValue(field: ContentFields, defaultLocaleCode: string, editor
|
|
|
180
189
|
}
|
|
181
190
|
|
|
182
191
|
interface ConvertFieldOptions {
|
|
183
|
-
field:
|
|
192
|
+
field: ContentfulField;
|
|
184
193
|
editorInterfaceControl?: Control;
|
|
185
194
|
cloudinaryImagesAsList: boolean;
|
|
186
195
|
bynderImagesAsList: boolean;
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
function convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList }: ConvertFieldOptions) {
|
|
190
|
-
const typedField = field
|
|
198
|
+
function convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList }: ConvertFieldOptions): FieldSpecificProps {
|
|
199
|
+
const typedField = field;
|
|
191
200
|
const type = typedField.type;
|
|
192
201
|
// return fieldConverterMap[typedField.type](typedField, editorInterfaceControl, cloudinaryImagesAsList);
|
|
193
202
|
switch (typedField.type) {
|
|
@@ -221,16 +230,19 @@ function convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, b
|
|
|
221
230
|
}
|
|
222
231
|
|
|
223
232
|
type FieldConverterMap = {
|
|
224
|
-
[
|
|
225
|
-
field:
|
|
233
|
+
[CtflField in ContentfulField as CtflField['type']]: (
|
|
234
|
+
field: CtflField,
|
|
226
235
|
editorInterfaceControl?: Control,
|
|
227
236
|
cloudinaryImagesAsList?: boolean,
|
|
228
237
|
bynderImagesAsList?: boolean
|
|
229
|
-
) =>
|
|
238
|
+
) => FieldSpecificProps;
|
|
230
239
|
};
|
|
231
240
|
|
|
232
241
|
const fieldConverterMap: FieldConverterMap = {
|
|
233
|
-
Symbol: function (
|
|
242
|
+
Symbol: function (
|
|
243
|
+
field,
|
|
244
|
+
editorInterfaceControl
|
|
245
|
+
): StackbitTypes.FieldStringProps | StackbitTypes.FieldSlugProps | StackbitTypes.FieldUrlProps | StackbitTypes.FieldEnumProps {
|
|
234
246
|
const options = getOptions(field);
|
|
235
247
|
if (options) {
|
|
236
248
|
return { type: 'enum', ...options };
|
|
@@ -245,55 +257,62 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
245
257
|
}
|
|
246
258
|
}
|
|
247
259
|
},
|
|
248
|
-
Text: function (
|
|
260
|
+
Text: function (
|
|
261
|
+
field,
|
|
262
|
+
editorInterfaceControl
|
|
263
|
+
): StackbitTypes.FieldStringProps | StackbitTypes.FieldTextProps | StackbitTypes.FieldMarkdownProps | StackbitTypes.FieldEnumProps {
|
|
249
264
|
const widgetId = _.get(editorInterfaceControl, 'widgetId');
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
} else if (widgetId === 'multipleLine') {
|
|
253
|
-
// can also be {type: 'html'} but we have no way of knowing that
|
|
254
|
-
return { type: 'text' };
|
|
255
|
-
} else if (widgetId === 'markdown') {
|
|
256
|
-
return { type: 'markdown' };
|
|
257
|
-
} else if (widgetId === 'dropdown' || widgetId === 'radio') {
|
|
265
|
+
const options = getOptions(field);
|
|
266
|
+
if (options) {
|
|
258
267
|
return {
|
|
259
268
|
type: 'enum',
|
|
260
|
-
...
|
|
269
|
+
...(widgetId === 'radio' ? { controlType: 'button-group' } : null),
|
|
270
|
+
...options
|
|
261
271
|
};
|
|
272
|
+
} else if (widgetId === 'singleLine') {
|
|
273
|
+
return { type: 'string' };
|
|
262
274
|
} else {
|
|
263
|
-
|
|
275
|
+
if (widgetId === 'multipleLine') {
|
|
276
|
+
// can also be {type: 'html'} but we have no way of knowing that
|
|
277
|
+
return { type: 'text' };
|
|
278
|
+
} else if (widgetId === 'markdown') {
|
|
279
|
+
return { type: 'markdown' };
|
|
280
|
+
} else {
|
|
281
|
+
return { type: 'text' };
|
|
282
|
+
}
|
|
264
283
|
}
|
|
265
284
|
},
|
|
266
|
-
Integer: function (field, editorInterfaceControl) {
|
|
267
|
-
const
|
|
268
|
-
if (
|
|
285
|
+
Integer: function (field, editorInterfaceControl): StackbitTypes.FieldNumberProps | StackbitTypes.FieldEnumProps {
|
|
286
|
+
const options = getOptions(field);
|
|
287
|
+
if (options) {
|
|
269
288
|
return {
|
|
270
289
|
type: 'enum',
|
|
271
|
-
...
|
|
290
|
+
...options
|
|
272
291
|
};
|
|
273
292
|
} else {
|
|
274
293
|
return {
|
|
275
294
|
type: 'number',
|
|
276
295
|
subtype: 'int',
|
|
277
|
-
...getMinMax(field)
|
|
296
|
+
...getMinMax(field) // backward compatibility
|
|
278
297
|
};
|
|
279
298
|
}
|
|
280
299
|
},
|
|
281
|
-
Number: function (field, editorInterfaceControl) {
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
300
|
+
Number: function (field, editorInterfaceControl): StackbitTypes.FieldNumberProps | StackbitTypes.FieldEnumProps {
|
|
301
|
+
const options = getOptions(field);
|
|
302
|
+
if (options) {
|
|
284
303
|
return {
|
|
285
304
|
type: 'enum',
|
|
286
|
-
...
|
|
305
|
+
...options
|
|
287
306
|
};
|
|
288
307
|
} else {
|
|
289
308
|
return {
|
|
290
309
|
type: 'number',
|
|
291
310
|
subtype: 'float',
|
|
292
|
-
...getMinMax(field)
|
|
311
|
+
...getMinMax(field) // backward compatibility
|
|
293
312
|
};
|
|
294
313
|
}
|
|
295
314
|
},
|
|
296
|
-
Date: function (field, editorInterfaceControl) {
|
|
315
|
+
Date: function (field, editorInterfaceControl): StackbitTypes.FieldDateProps | StackbitTypes.FieldDatetimeProps {
|
|
297
316
|
const format = _.get(editorInterfaceControl, 'settings.format');
|
|
298
317
|
const type = format === 'dateonly' ? 'date' : 'datetime';
|
|
299
318
|
return { type: type };
|
|
@@ -301,10 +320,10 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
301
320
|
Location: function () {
|
|
302
321
|
return { type: 'json' };
|
|
303
322
|
},
|
|
304
|
-
Boolean: function () {
|
|
323
|
+
Boolean: function (): StackbitTypes.FieldBooleanProps {
|
|
305
324
|
return { type: 'boolean' };
|
|
306
325
|
},
|
|
307
|
-
Link: function (field) {
|
|
326
|
+
Link: function (field): StackbitTypes.FieldImageProps | StackbitTypes.FieldReferenceProps {
|
|
308
327
|
const linkType = field.linkType;
|
|
309
328
|
if (linkType === 'Asset') {
|
|
310
329
|
return { type: 'image' };
|
|
@@ -320,17 +339,17 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
320
339
|
throw new Error(`not supported linkType: ${linkType}`);
|
|
321
340
|
}
|
|
322
341
|
},
|
|
323
|
-
ResourceLink: function (field) {
|
|
342
|
+
ResourceLink: function (field): StackbitTypes.FieldCrossReferenceProps {
|
|
324
343
|
return {
|
|
325
344
|
type: 'cross-reference',
|
|
326
|
-
models: field.allowedResources.reduce((models: FieldCrossReferenceModel[], allowedResource) => {
|
|
345
|
+
models: field.allowedResources.reduce((models: StackbitTypes.FieldCrossReferenceModel[], allowedResource) => {
|
|
327
346
|
const match = allowedResource.source.match(/crn:contentful:::content:spaces\/(.+)$/);
|
|
328
347
|
if (!match) {
|
|
329
348
|
return models;
|
|
330
349
|
}
|
|
331
350
|
const spaceId = match[1]!;
|
|
332
351
|
return models.concat(
|
|
333
|
-
allowedResource.contentTypes.reduce((models: FieldCrossReferenceModel[], contentType) => {
|
|
352
|
+
allowedResource.contentTypes.reduce((models: StackbitTypes.FieldCrossReferenceModel[], contentType) => {
|
|
334
353
|
return models.concat({
|
|
335
354
|
modelName: contentType,
|
|
336
355
|
srcType: 'contentful',
|
|
@@ -341,9 +360,13 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
341
360
|
}, [])
|
|
342
361
|
};
|
|
343
362
|
},
|
|
344
|
-
Array: function (field, editorInterfaceControl) {
|
|
363
|
+
Array: function (field, editorInterfaceControl): StackbitTypes.FieldListProps {
|
|
345
364
|
const widgetId = _.get(editorInterfaceControl, 'widgetId');
|
|
346
|
-
const items = getListItems(field);
|
|
365
|
+
const items = getListItems(field) as StackbitTypes.FieldListItems;
|
|
366
|
+
const validations = convertValidations(field.items.validations);
|
|
367
|
+
if (validations) {
|
|
368
|
+
items.validations = validations;
|
|
369
|
+
}
|
|
347
370
|
if (widgetId === 'checkbox' && items.type === 'enum') {
|
|
348
371
|
return {
|
|
349
372
|
type: 'list',
|
|
@@ -356,7 +379,7 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
356
379
|
items
|
|
357
380
|
};
|
|
358
381
|
},
|
|
359
|
-
Object: function (field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList) {
|
|
382
|
+
Object: function (field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList): StackbitTypes.FieldSpecificProps {
|
|
360
383
|
const widgetId = _.get(editorInterfaceControl, 'widgetId');
|
|
361
384
|
if (widgetId === CONTENTFUL_CLOUDINARY_APP) {
|
|
362
385
|
if (cloudinaryImagesAsList) {
|
|
@@ -411,7 +434,7 @@ const fieldConverterMap: FieldConverterMap = {
|
|
|
411
434
|
}
|
|
412
435
|
};
|
|
413
436
|
|
|
414
|
-
function getMinMax(field:
|
|
437
|
+
function getMinMax(field: ContentfulField) {
|
|
415
438
|
const validations = field.validations;
|
|
416
439
|
const rangeValidation = _.find(validations, 'range');
|
|
417
440
|
return rangeValidation
|
|
@@ -422,24 +445,24 @@ function getMinMax(field: ContentFields) {
|
|
|
422
445
|
: null;
|
|
423
446
|
}
|
|
424
447
|
|
|
425
|
-
function getOptions(field:
|
|
448
|
+
function getOptions(field: ContentfulField) {
|
|
426
449
|
const validations = field.validations;
|
|
427
450
|
const inValidation = _.find(validations, 'in');
|
|
428
|
-
return inValidation ? { options: inValidation.in
|
|
451
|
+
return inValidation?.in && inValidation.in.length > 0 ? { options: inValidation.in } : null;
|
|
429
452
|
}
|
|
430
453
|
|
|
431
|
-
function getListItems(field:
|
|
454
|
+
function getListItems(field: ContentfulField & Extract<ExtendedFieldType, { type: 'Array' }>) {
|
|
432
455
|
const items = field.items;
|
|
433
456
|
const itemType = items.type;
|
|
434
457
|
if (items.type === 'Symbol') {
|
|
435
|
-
return fieldConverterMap.Symbol(items as
|
|
458
|
+
return fieldConverterMap.Symbol(items as ContentfulField & Extract<ExtendedFieldType, { type: 'Symbol' }>);
|
|
436
459
|
} else if (items.type === 'Link') {
|
|
437
|
-
return fieldConverterMap.Link(items as
|
|
460
|
+
return fieldConverterMap.Link(items as ContentfulField & Extract<ExtendedFieldType, { type: 'Link' }>);
|
|
438
461
|
} else if (items.type === 'ResourceLink') {
|
|
439
462
|
return fieldConverterMap.ResourceLink({
|
|
440
463
|
...items,
|
|
441
464
|
allowedResources: (field as ResourceLinkArray).allowedResources
|
|
442
|
-
} as
|
|
465
|
+
} as ContentfulField & Extract<ExtendedFieldType, { type: 'ResourceLink' }>);
|
|
443
466
|
} else {
|
|
444
467
|
throw new Error(`not supported list items.type: ${itemType}, fieldId: ${field.id}`);
|
|
445
468
|
}
|
|
@@ -465,3 +488,141 @@ function resolveLabelFieldForModel(contentType: ContentTypeProps, modelLabelFiel
|
|
|
465
488
|
}
|
|
466
489
|
return labelField;
|
|
467
490
|
}
|
|
491
|
+
|
|
492
|
+
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
|
|
493
|
+
type StackbitFieldValidations =
|
|
494
|
+
| StackbitTypes.FieldValidationsUnique
|
|
495
|
+
| StackbitTypes.FieldValidationsRegExp
|
|
496
|
+
| StackbitTypes.FieldValidationsStringLength
|
|
497
|
+
| StackbitTypes.FieldValidationsListLength
|
|
498
|
+
| StackbitTypes.FieldValidationsDateRange
|
|
499
|
+
| StackbitTypes.FieldValidationsNumberRange
|
|
500
|
+
| StackbitTypes.FieldValidationsFile
|
|
501
|
+
| StackbitTypes.FieldValidationsImage;
|
|
502
|
+
type ValidationsIntersection = UnionToIntersection<StackbitFieldValidations>;
|
|
503
|
+
|
|
504
|
+
function convertValidations(validations: ContentTypeFieldValidation[] | undefined): StackbitTypes.Field['validations'] {
|
|
505
|
+
if (!validations) {
|
|
506
|
+
return undefined;
|
|
507
|
+
}
|
|
508
|
+
const stackbitValidations: RequiredBy<ValidationsIntersection, 'errors'> = {
|
|
509
|
+
errors: {}
|
|
510
|
+
};
|
|
511
|
+
for (const validation of validations) {
|
|
512
|
+
if (validation.unique) {
|
|
513
|
+
stackbitValidations.unique = true;
|
|
514
|
+
} else if (validation.size) {
|
|
515
|
+
const size = validation.size;
|
|
516
|
+
const message = validation.message;
|
|
517
|
+
if (!_.isNil(size.min)) {
|
|
518
|
+
(stackbitValidations as StackbitTypes.FieldValidationsStringLength).min = size.min;
|
|
519
|
+
stackbitValidations.errors.min = message;
|
|
520
|
+
}
|
|
521
|
+
if (!_.isNil(size.max)) {
|
|
522
|
+
(stackbitValidations as StackbitTypes.FieldValidationsStringLength).max = size.max;
|
|
523
|
+
stackbitValidations.errors.max = message;
|
|
524
|
+
}
|
|
525
|
+
} else if (validation.range) {
|
|
526
|
+
const range = validation.range;
|
|
527
|
+
const message = validation.message;
|
|
528
|
+
if (!_.isNil(range.min)) {
|
|
529
|
+
(stackbitValidations as StackbitTypes.FieldValidationsNumberRange).min = range.min;
|
|
530
|
+
stackbitValidations.errors.min = message;
|
|
531
|
+
}
|
|
532
|
+
if (!_.isNil(range.max)) {
|
|
533
|
+
(stackbitValidations as StackbitTypes.FieldValidationsNumberRange).max = range.max;
|
|
534
|
+
stackbitValidations.errors.max = message;
|
|
535
|
+
}
|
|
536
|
+
} else if (validation.dateRange) {
|
|
537
|
+
const dateRange = validation.dateRange;
|
|
538
|
+
const message = validation.message;
|
|
539
|
+
if (!_.isNil(dateRange.min)) {
|
|
540
|
+
(stackbitValidations as StackbitTypes.FieldValidationsDateRange).min = dateRange.min;
|
|
541
|
+
stackbitValidations.errors.min = message;
|
|
542
|
+
}
|
|
543
|
+
if (!_.isNil(dateRange.max)) {
|
|
544
|
+
(stackbitValidations as StackbitTypes.FieldValidationsDateRange).max = dateRange.max;
|
|
545
|
+
stackbitValidations.errors.max = message;
|
|
546
|
+
}
|
|
547
|
+
} else if (validation.regexp) {
|
|
548
|
+
stackbitValidations.regexp = validation.regexp.pattern;
|
|
549
|
+
stackbitValidations.errors.regexp = validation.message;
|
|
550
|
+
} else if (validation.prohibitRegexp) {
|
|
551
|
+
stackbitValidations.regexpNot = validation.prohibitRegexp.pattern;
|
|
552
|
+
stackbitValidations.errors.regexpNot = validation.message;
|
|
553
|
+
} else if (validation.assetImageDimensions) {
|
|
554
|
+
const message = validation.message;
|
|
555
|
+
const { width, height } = validation.assetImageDimensions;
|
|
556
|
+
stackbitValidations.minWidth = width?.min;
|
|
557
|
+
stackbitValidations.maxWidth = width?.max;
|
|
558
|
+
stackbitValidations.minHeight = height?.min;
|
|
559
|
+
stackbitValidations.maxHeight = height?.max;
|
|
560
|
+
stackbitValidations.errors.minWidth = _.isNil(width?.min) ? undefined : message;
|
|
561
|
+
stackbitValidations.errors.maxWidth = _.isNil(width?.max) ? undefined : message;
|
|
562
|
+
stackbitValidations.errors.minHeight = _.isNil(height?.min) ? undefined : message;
|
|
563
|
+
stackbitValidations.errors.maxHeight = _.isNil(height?.max) ? undefined : message;
|
|
564
|
+
} else if (validation.assetFileSize) {
|
|
565
|
+
const message = validation.message;
|
|
566
|
+
const { min, max } = validation.assetFileSize;
|
|
567
|
+
stackbitValidations.fileMinSize = min;
|
|
568
|
+
stackbitValidations.fileMaxSize = max;
|
|
569
|
+
stackbitValidations.errors.fileMinSize = _.isNil(min) ? undefined : message;
|
|
570
|
+
stackbitValidations.errors.fileMaxSize = _.isNil(max) ? undefined : message;
|
|
571
|
+
} else if (validation.linkMimetypeGroup) {
|
|
572
|
+
stackbitValidations.fileTypeGroups = convertMimeTypesToFileTypeGroups(validation.linkMimetypeGroup);
|
|
573
|
+
stackbitValidations.errors.fileTypeGroups = validation.message;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return undefinedIfEmpty(
|
|
577
|
+
omitByNil({
|
|
578
|
+
...stackbitValidations,
|
|
579
|
+
errors: undefinedIfEmpty(omitByNil(stackbitValidations.errors))
|
|
580
|
+
})
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function convertMimeTypesToFileTypeGroups(mimeTypes?: string[]): StackbitTypes.FieldValidationsFileTypesGroup[] | undefined {
|
|
585
|
+
// contentful mimeTypes:
|
|
586
|
+
// "image", "audio", "video", "plaintext", "markup", "richtext", "code",
|
|
587
|
+
// "pdfdocument", "presentation", "spreadsheet", "attachment", "archive"
|
|
588
|
+
const map: Record<string, StackbitTypes.FieldValidationsFileTypesGroup> = {
|
|
589
|
+
image: 'image',
|
|
590
|
+
video: 'video',
|
|
591
|
+
audio: 'audio',
|
|
592
|
+
plaintext: 'text',
|
|
593
|
+
markup: 'markup',
|
|
594
|
+
code: 'code',
|
|
595
|
+
pdfdocument: 'document',
|
|
596
|
+
presentation: 'presentation',
|
|
597
|
+
spreadsheet: 'spreadsheet',
|
|
598
|
+
archive: 'archive'
|
|
599
|
+
};
|
|
600
|
+
const fileTypeGroups = (mimeTypes ?? []).reduce((accum: StackbitTypes.FieldValidationsFileTypesGroup[], mimeType) => {
|
|
601
|
+
const fileTypeGroup = map[mimeType];
|
|
602
|
+
if (fileTypeGroup) {
|
|
603
|
+
accum.push(fileTypeGroup);
|
|
604
|
+
}
|
|
605
|
+
return accum;
|
|
606
|
+
}, []);
|
|
607
|
+
return undefinedIfEmpty(fileTypeGroups);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function wrapWithMessageIfNeeded<V extends string | number | string[]>(
|
|
611
|
+
value: V | undefined,
|
|
612
|
+
message: string | undefined
|
|
613
|
+
): V | { value: V; message: string } | undefined {
|
|
614
|
+
if (typeof value === 'undefined' || value === null) {
|
|
615
|
+
return undefined;
|
|
616
|
+
}
|
|
617
|
+
if (message) {
|
|
618
|
+
return {
|
|
619
|
+
value,
|
|
620
|
+
message
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
return value;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function undefinedIfEmpty<T extends Record<string, unknown> | unknown[]>(value: T): T | undefined {
|
|
627
|
+
return _.isEmpty(value) ? undefined : value;
|
|
628
|
+
}
|