@stackbit/cms-contentful
Version:
Stackbit Contentful CMS Interface
519 lines • 20.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertSchema = void 0;
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
};
}
exports.convertSchema = convertSchema;
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 = 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