UNPKG

@stackbit/cms-contentful

Version:

Stackbit Contentful CMS Interface

518 lines 20.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertSchema = convertSchema; const lodash_1 = __importDefault(require("lodash")); const utils_1 = require("@stackbit/utils"); const contentful_consts_1 = require("./contentful-consts"); function convertSchema({ contentTypes, editorInterfaces, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList }) { const editorInterfaceByContentTypeId = lodash_1.default.chain(editorInterfaces) .filter({ sys: { type: 'EditorInterface', contentType: { sys: { type: 'Link', linkType: 'ContentType' } } } }) .keyBy('sys.contentType.sys.id') .value(); const models = lodash_1.default.map(contentTypes, (contentType) => mapModel({ contentType, editorInterfaceByContentTypeId, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList })); return { models }; } function mapModel(options) { const contentType = options.contentType; const contentTypeId = contentType.sys.id; const mappedFields = mapFields(options); return Object.assign({ type: 'data', name: contentTypeId }, (0, utils_1.omitByNil)({ label: contentType.name ?? lodash_1.default.startCase(contentTypeId), description: contentType.description ?? null, labelField: resolveLabelFieldForModel(contentType, 'displayField', mappedFields), fields: mappedFields })); } function mapFields({ contentType, editorInterfaceByContentTypeId, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList }) { const contentTypeId = contentType.sys.id; const fields = contentType.fields; const editorInterfaceControls = editorInterfaceByContentTypeId[contentTypeId]?.controls; const editorInterfaceControlsByFieldId = lodash_1.default.keyBy(editorInterfaceControls, 'fieldId'); return lodash_1.default.map(fields, (field) => mapField({ field, editorInterfaceControlsByFieldId, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList })); } function mapField({ field, editorInterfaceControlsByFieldId, defaultLocaleCode, cloudinaryImagesAsList, bynderImagesAsList }) { const fieldId = field.id; const editorInterfaceControl = editorInterfaceControlsByFieldId[fieldId]; const isRequired = field.required; const isReadonly = lodash_1.default.get(editorInterfaceControl, 'settings.readOnly'); const isHidden = field.disabled ?? isReadonly; const validations = field.validations; const localized = field.localized; const defaultValue = getDefaultValue(field, defaultLocaleCode, editorInterfaceControl); const defaultAsConst = isRequired && isReadonly && !lodash_1.default.isUndefined(defaultValue); const fieldSpecificProps = convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList }); return lodash_1.default.assign({ type: null, name: fieldId }, (0, utils_1.omitByNil)({ label: field.name ?? lodash_1.default.startCase(fieldId), description: lodash_1.default.get(editorInterfaceControl, 'settings.helpText'), required: isRequired, default: defaultAsConst ? undefined : defaultValue, const: defaultAsConst ? defaultValue : undefined, readOnly: isReadonly, hidden: isHidden, validations: convertValidations(validations), localized: localized }), fieldSpecificProps); } function getDefaultValue(field, defaultLocaleCode, editorInterfaceControl) { // Contentful defaultValue is an object with locale keys and default values per locale const defaultValue = field.defaultValue; if (typeof defaultValue !== 'undefined') { return lodash_1.default.isPlainObject(defaultValue) && defaultLocaleCode in defaultValue ? defaultValue[defaultLocaleCode] : undefined; } const widgetId = editorInterfaceControl?.widgetId; if (widgetId === 'default-boolean-value') { return lodash_1.default.get(editorInterfaceControl, 'settings.defaultValue'); } else if (widgetId === 'default-field-value') { return lodash_1.default.get(editorInterfaceControl, 'settings.defaultValue'); } else { return undefined; } } function convertField({ field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList }) { const typedField = field; const type = typedField.type; // return fieldConverterMap[typedField.type](typedField, editorInterfaceControl, cloudinaryImagesAsList); switch (typedField.type) { case 'Symbol': return fieldConverterMap.Symbol(typedField, editorInterfaceControl); case 'Text': return fieldConverterMap.Text(typedField, editorInterfaceControl); case 'Integer': return fieldConverterMap.Integer(typedField, editorInterfaceControl); case 'Number': return fieldConverterMap.Number(typedField, editorInterfaceControl); case 'Date': return fieldConverterMap.Date(typedField, editorInterfaceControl); case 'Location': return fieldConverterMap.Location(typedField, editorInterfaceControl); case 'Boolean': return fieldConverterMap.Boolean(typedField, editorInterfaceControl); case 'Link': return fieldConverterMap.Link(typedField, editorInterfaceControl); case 'ResourceLink': return fieldConverterMap.ResourceLink(typedField, editorInterfaceControl); case 'Array': return fieldConverterMap.Array(typedField, editorInterfaceControl); case 'Object': return fieldConverterMap.Object(typedField, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList); case 'RichText': return fieldConverterMap.RichText(typedField, editorInterfaceControl); default: throw new Error(`field type '${type}' not supported, fieldId: ${field.id}`); } } const fieldConverterMap = { Symbol: function (field, editorInterfaceControl) { const options = getOptions(field); if (options) { return { type: 'enum', ...options }; } else { const widgetId = lodash_1.default.get(editorInterfaceControl, 'widgetId'); if (widgetId === 'slugEditor') { return { type: 'slug' }; } else if (widgetId === 'urlEditor') { return { type: 'url' }; } else { return { type: 'string' }; } } }, Text: function (field, editorInterfaceControl) { const widgetId = lodash_1.default.get(editorInterfaceControl, 'widgetId'); const options = getOptions(field); if (options) { return { type: 'enum', ...(widgetId === 'radio' ? { controlType: 'button-group' } : null), ...options }; } else if (widgetId === 'singleLine') { return { type: 'string' }; } else { if (widgetId === 'multipleLine') { // can also be {type: 'html'} but we have no way of knowing that return { type: 'text' }; } else if (widgetId === 'markdown') { return { type: 'markdown' }; } else { return { type: 'text' }; } } }, Integer: function (field, editorInterfaceControl) { const options = getOptions(field); if (options) { return { type: 'enum', ...options }; } else { return { type: 'number', subtype: 'int', ...getMinMax(field) // backward compatibility }; } }, Number: function (field, editorInterfaceControl) { const options = getOptions(field); if (options) { return { type: 'enum', ...options }; } else { return { type: 'number', subtype: 'float', ...getMinMax(field) // backward compatibility }; } }, Date: function (field, editorInterfaceControl) { const format = lodash_1.default.get(editorInterfaceControl, 'settings.format'); const type = format === 'dateonly' ? 'date' : 'datetime'; return { type: type }; }, Location: function () { return { type: 'json' }; }, Boolean: function () { return { type: 'boolean' }; }, Link: function (field) { const linkType = field.linkType; if (linkType === 'Asset') { return { type: 'image' }; } else if (linkType === 'Entry') { const validations = field.validations ?? []; const linkContentTypeValidation = lodash_1.default.find(validations, 'linkContentType'); const linkContentTypes = linkContentTypeValidation?.linkContentType ?? []; return { type: 'reference', models: linkContentTypes }; } else { throw new Error(`not supported linkType: ${linkType}`); } }, ResourceLink: function (field) { return { type: 'cross-reference', models: field.allowedResources?.reduce((models, allowedResource) => { const match = 'source' in allowedResource && allowedResource.source.match(/crn:contentful:::content:spaces\/(.+)$/); if (!match) { return models; } const spaceId = match[1]; return models.concat(allowedResource.contentTypes.reduce((models, contentType) => { return models.concat({ modelName: contentType, srcType: 'contentful', srcProjectId: spaceId }); }, [])); }, []) ?? [] }; }, Array: function (field, editorInterfaceControl) { const widgetId = lodash_1.default.get(editorInterfaceControl, 'widgetId'); const items = getListItems(field); const validations = convertValidations(field.items.validations); if (validations) { items.validations = validations; } if (widgetId === 'checkbox' && items.type === 'enum') { return { type: 'list', controlType: 'checkbox', items }; } return { type: 'list', items }; }, Object: function (field, editorInterfaceControl, cloudinaryImagesAsList, bynderImagesAsList) { const widgetId = lodash_1.default.get(editorInterfaceControl, 'widgetId'); if (widgetId === contentful_consts_1.CONTENTFUL_CLOUDINARY_APP) { if (cloudinaryImagesAsList) { return { type: 'list', items: { type: 'image', source: 'cloudinary' } }; } return { type: 'image', source: 'cloudinary' }; } else if (widgetId === contentful_consts_1.CONTENTFUL_BYNDER_APP) { if (bynderImagesAsList) { return { type: 'list', items: { type: 'image', source: 'bynder' } }; } return { type: 'image', source: 'bynder' }; } else if (widgetId === contentful_consts_1.CONTENTFUL_APRIMO_APP) { return { type: 'list', items: { type: 'image', source: 'aprimo' } }; } return { type: 'json' }; }, RichText: function (field) { const validations = lodash_1.default.get(field, 'validations'); const nodeTypesValidation = lodash_1.default.find(validations, 'enabledNodeTypes'); const marksValidation = lodash_1.default.find(validations, 'enabledMarks'); return { type: 'richText', options: { nodes: (nodeTypesValidation?.enabledNodeTypes ?? []).map((node) => lodash_1.default.get(contentful_consts_1.CONTENTFUL_NODE_TYPES_MAP, node, 'unsupported')), marks: (marksValidation?.enabledMarks ?? []).map((mark) => lodash_1.default.get(contentful_consts_1.CONTENTFUL_MARKS_TYPES_MAP, mark, 'unsupported')) } }; } }; function getMinMax(field) { const validations = field.validations; const rangeValidation = lodash_1.default.find(validations, 'range'); return rangeValidation ? { min: rangeValidation.range.min, max: rangeValidation.range.max } : null; } function getOptions(field) { const validations = field.validations; const inValidation = lodash_1.default.find(validations, 'in'); return inValidation?.in && inValidation.in.length > 0 ? { options: inValidation.in } : null; } function getListItems(field) { const items = field.items; const itemType = items.type; if (items.type === 'Symbol') { return fieldConverterMap.Symbol(items); } else if (items.type === 'Link') { return fieldConverterMap.Link(items); } else if (items.type === 'ResourceLink') { return fieldConverterMap.ResourceLink({ ...items, allowedResources: field.allowedResources }); } else { throw new Error(`not supported list items.type: ${itemType}, fieldId: ${field.id}`); } } function resolveLabelFieldForModel(contentType, modelLabelFieldPath, fields) { let labelField = contentType.displayField ?? null; if (labelField) { return labelField; } // see if there is a field named 'title' let titleField = lodash_1.default.find(fields, (field) => field.name === 'title' && ['string', 'text'].includes(field.type)); if (!titleField) { // see if there is a field named 'label' titleField = lodash_1.default.find(fields, (field) => field.name === 'label' && ['string', 'text'].includes(field.type)); } if (!titleField) { // get the first 'string' field titleField = lodash_1.default.find(fields, { type: 'string' }); } if (titleField) { labelField = lodash_1.default.get(titleField, 'name'); } return labelField; } function convertValidations(validations) { if (!validations) { return undefined; } const stackbitValidations = { errors: {} }; for (const validation of validations) { if (validation.unique) { stackbitValidations.unique = true; } else if (validation.size) { const size = validation.size; const message = validation.message; if (!lodash_1.default.isNil(size.min)) { stackbitValidations.min = size.min; stackbitValidations.errors.min = message; } if (!lodash_1.default.isNil(size.max)) { stackbitValidations.max = size.max; stackbitValidations.errors.max = message; } } else if (validation.range) { const range = validation.range; const message = validation.message; if (!lodash_1.default.isNil(range.min)) { stackbitValidations.min = range.min; stackbitValidations.errors.min = message; } if (!lodash_1.default.isNil(range.max)) { stackbitValidations.max = range.max; stackbitValidations.errors.max = message; } } else if (validation.dateRange) { const dateRange = validation.dateRange; const message = validation.message; if (!lodash_1.default.isNil(dateRange.min)) { stackbitValidations.min = dateRange.min; stackbitValidations.errors.min = message; } if (!lodash_1.default.isNil(dateRange.max)) { stackbitValidations.max = dateRange.max; stackbitValidations.errors.max = message; } } else if (validation.regexp) { stackbitValidations.regexp = validation.regexp.pattern; stackbitValidations.errors.regexp = validation.message; } else if (validation.prohibitRegexp) { stackbitValidations.regexpNot = validation.prohibitRegexp.pattern; stackbitValidations.errors.regexpNot = validation.message; } else if (validation.assetImageDimensions) { const message = validation.message; const { width, height } = validation.assetImageDimensions; stackbitValidations.minWidth = width?.min; stackbitValidations.maxWidth = width?.max; stackbitValidations.minHeight = height?.min; stackbitValidations.maxHeight = height?.max; stackbitValidations.errors.minWidth = lodash_1.default.isNil(width?.min) ? undefined : message; stackbitValidations.errors.maxWidth = lodash_1.default.isNil(width?.max) ? undefined : message; stackbitValidations.errors.minHeight = lodash_1.default.isNil(height?.min) ? undefined : message; stackbitValidations.errors.maxHeight = lodash_1.default.isNil(height?.max) ? undefined : message; } else if (validation.assetFileSize) { const message = validation.message; const { min, max } = validation.assetFileSize; stackbitValidations.fileMinSize = min; stackbitValidations.fileMaxSize = max; stackbitValidations.errors.fileMinSize = lodash_1.default.isNil(min) ? undefined : message; stackbitValidations.errors.fileMaxSize = lodash_1.default.isNil(max) ? undefined : message; } else if (validation.linkMimetypeGroup) { stackbitValidations.fileTypeGroups = convertMimeTypesToFileTypeGroups(validation.linkMimetypeGroup); stackbitValidations.errors.fileTypeGroups = validation.message; } } return undefinedIfEmpty((0, utils_1.omitByNil)({ ...stackbitValidations, errors: undefinedIfEmpty((0, utils_1.omitByNil)(stackbitValidations.errors)) })); } function convertMimeTypesToFileTypeGroups(mimeTypes) { // contentful mimeTypes: // "image", "audio", "video", "plaintext", "markup", "richtext", "code", // "pdfdocument", "presentation", "spreadsheet", "attachment", "archive" const map = { image: 'image', video: 'video', audio: 'audio', plaintext: 'text', markup: 'markup', code: 'code', pdfdocument: 'document', presentation: 'presentation', spreadsheet: 'spreadsheet', archive: 'archive' }; const fileTypeGroups = (mimeTypes ?? []).reduce((accum, mimeType) => { const fileTypeGroup = map[mimeType]; if (fileTypeGroup) { accum.push(fileTypeGroup); } return accum; }, []); return undefinedIfEmpty(fileTypeGroups); } function wrapWithMessageIfNeeded(value, message) { if (typeof value === 'undefined' || value === null) { return undefined; } if (message) { return { value, message }; } return value; } function undefinedIfEmpty(value) { return lodash_1.default.isEmpty(value) ? undefined : value; } //# sourceMappingURL=contentful-schema-converter.js.map