UNPKG

@stackbit/cms-contentful

Version:

Stackbit Contentful CMS Interface

416 lines 15.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.convertEntities = convertEntities; exports.convertAssets = convertAssets; exports.convertEntry = convertEntry; exports.convertAsset = convertAsset; exports.convertDocumentVersions = convertDocumentVersions; const lodash_1 = __importDefault(require("lodash")); const contentful_consts_1 = require("./contentful-consts"); function convertEntities({ entries, getModelByName, userMap, defaultLocale, projectUrl }) { return entries .map((entry) => { return convertEntry({ entry, getModelByName, userMap, defaultLocale, projectUrl }); }) .filter((document) => !!document); } function convertAssets({ assets, userMap, defaultLocale, projectUrl }) { return assets .map((asset) => { return convertAsset({ asset, userMap, defaultLocale, projectUrl }); }) .filter((asset) => !!asset); } function convertEntry({ entry, getModelByName, userMap, defaultLocale, projectUrl }) { if (!entry) { return; } const contentTypeId = getEntryContentTypeId(entry); const model = getModelByName(contentTypeId); if (!model) { return; } const entryId = getEntityId(entry); const environment = getEntryEnvironmentId(entry); const envUrlPath = environment && environment !== 'master' ? `/environments/${environment}` : ''; const objectUrl = `${projectUrl}${envUrlPath}/entries/${entryId}`; return { type: 'document', id: entryId, manageUrl: objectUrl, modelName: model.name, ...commonFields(entry, userMap), fields: convertFields({ fields: entry.fields, model, defaultLocale }), context: { sys: { type: entry.sys.type, version: entry.sys.version, publishedVersion: entry.sys.publishedVersion, archivedVersion: entry.sys.archivedVersion } } }; } function convertAsset({ asset, userMap, defaultLocale, projectUrl }) { if (!asset) { return; } const assetId = getEntityId(asset); const environment = getEntryEnvironmentId(asset); const envUrlPath = environment && environment !== 'master' ? `/environments/${environment}` : ''; const objectUrl = `${projectUrl}${envUrlPath}/assets/${assetId}`; // Remap Contentful Asset fields to Stackbit AssetFields // ContentfulAsset.fields: { // title: { // "en-US": "...", // "en-GB": "..." // }, // file: { // "en-US": { url: "..." }, // "en-GB": { url: "..." } // } // } // => StackbitAsset.fields: { // title: { // "type": "string", // "localized": true, // "locales": [ // { "locale": "en-US", "value": "..." }, // { "locale": "en-GB", "value": "..." } // ] // }, // url: { // "type": "string", // "localized": true, // "locales": [ // { "locale": "en-US", "value": "..." }, // { "locale": "en-GB", "value": "..." } // ] // } // } const fileField = toDocumentField(asset.fields.file, true, defaultLocale, { type: 'assetFile' }, (val) => ({ url: val.url ?? '', fileName: val.fileName, contentType: val.contentType, size: val.details?.size, dimensions: { width: val.details?.image?.width, height: val.details?.image?.height } })); if (!fileField) { return; } const titleField = toDocumentField(asset.fields.title, true, defaultLocale, { type: 'string' }, (val) => ({ value: val })) ?? { type: 'string', value: null }; const descriptionField = toDocumentField(asset.fields.description, true, defaultLocale, { type: 'text' }, (val) => ({ value: val })) ?? { type: 'text', value: null }; return { type: 'asset', id: assetId, manageUrl: objectUrl, ...commonFields(asset, userMap), fields: { title: titleField, description: descriptionField, file: fileField }, context: { sys: { type: asset.sys.type, version: asset.sys.version, publishedVersion: asset.sys.publishedVersion, archivedVersion: asset.sys.archivedVersion } } }; } function commonFields(entity, userMap) { // currently we only expose isChanged that is treated as isDraft and isChanged because in Sanity there is no way to tell the difference const isArchived = !!entity.sys?.archivedVersion; const isPublished = !!entity.sys?.publishedVersion; // unpublished and deleted appear the same in contentful's API response. const isChanged = !!entity.sys?.publishedVersion && entity.sys?.version >= entity.sys?.publishedVersion + 2; const status = isChanged ? 'modified' : isArchived ? 'archived' : isPublished ? 'published' : 'added'; // draft is the remaining possible status due to isArchived and isPublished being false. const createdByEmail = getUserEmailById(entity.sys?.createdBy?.sys?.id, userMap); const updatedByEmail = getUserEmailById(entity.sys?.updatedBy?.sys?.id, userMap); const updatedByList = updatedByEmail ? [updatedByEmail] : []; return { status: status, createdAt: entity.sys?.createdAt ?? null, createdBy: createdByEmail, updatedAt: entity.sys?.updatedAt ?? null, updatedBy: updatedByList }; } function getEntityId(entity) { return entity.sys?.id; } function getEntryEnvironmentId(entity) { return entity.sys?.environment?.sys?.id; } function getEntryContentTypeId(entry) { return entry.sys?.contentType?.sys?.id; } function getUserEmailById(userId, userMap) { if (!userId) { return; } return userMap[userId]?.email; } function convertFields({ fields, model, defaultLocale }) { const fieldsByName = lodash_1.default.keyBy(model.fields, 'name'); return lodash_1.default.reduce(fields, (documentFields, value, fieldName) => { const modelField = fieldsByName[fieldName]; if (!modelField) { return documentFields; // TODO: log user error // throw new Error(`Error in contentful-entries-converter, no model field found for entry field name ${fieldName}`); } const documentField = convertFieldType({ value, modelField, defaultLocale, localized: !!modelField.localized }); if (documentField) { documentFields[fieldName] = documentField; } return documentFields; }, {}); } function convertFieldType({ value, modelField, defaultLocale, localized }) { if (modelField.type === 'list') { const itemsModel = modelField.items ?? { type: 'string' }; return toDocumentField(value, localized, defaultLocale, { type: 'list' }, (val) => ({ items: lodash_1.default.map(val, (item) => { if (itemsModel.type === 'reference') { return { type: 'reference', refType: 'document', refId: lodash_1.default.get(item, 'sys.id') }; } else if (itemsModel.type === 'cross-reference') { const urn = lodash_1.default.get(item, 'sys.urn'); return { type: 'cross-reference', refType: 'document', ...crossReferenceFieldPropsFromUrn(urn) }; } else if (itemsModel.type === 'image') { if (itemsModel.source === 'cloudinary') { return { type: 'image', source: itemsModel.source, sourceData: item }; } if (itemsModel.source === 'bynder') { return { type: 'image', source: itemsModel.source, sourceData: item, fields: { title: { type: 'string', value: item?.name }, url: { type: 'string', value: item?.src } } }; } if (itemsModel.source === 'aprimo') { return { type: 'image', source: itemsModel.source, sourceData: item, fields: { title: { type: 'string', value: item?.title }, url: { type: 'string', value: item?.rendition?.publicuri } } }; } return { type: 'reference', refType: 'asset', refId: lodash_1.default.get(item, 'sys.id') }; } else if (itemsModel.type === 'richText') { return { type: 'richText', hint: flattenRichText(item).substring(0, 200), value: item }; } return { type: itemsModel.type, value: item }; }) })); } else if (modelField.type === 'reference') { return toDocumentField(value, localized, defaultLocale, { type: 'reference', refType: 'document' }, (val) => ({ refId: lodash_1.default.get(val, 'sys.id') })); } else if (modelField.type === 'cross-reference') { return toDocumentField(value, localized, defaultLocale, { type: 'cross-reference', refType: 'document' }, (val) => { const urn = lodash_1.default.get(val, 'sys.urn'); return crossReferenceFieldPropsFromUrn(urn); }); } else if (modelField.type === 'image') { if (modelField.source && contentful_consts_1.CONTENTFUL_BUILT_IN_IMAGE_SOURCES.includes(modelField.source)) { if (modelField.source === 'bynder') { return toDocumentField(value, localized, defaultLocale, { type: 'image', source: modelField.source }, (val) => ({ fields: { title: { type: 'string', value: val[0]?.name }, url: { type: 'string', value: val[0]?.src } }, sourceData: val[0] })); } return toDocumentField(value, localized, defaultLocale, { type: 'image', source: modelField.source }, (val) => ({ sourceData: val[0] })); } return toDocumentField(value, localized, defaultLocale, { type: 'reference', refType: 'asset' }, (val) => ({ refId: lodash_1.default.get(val, 'sys.id') })); } else if (modelField.type === 'richText') { return toDocumentField(value, localized, defaultLocale, { type: 'richText' }, (val) => ({ hint: flattenRichText(val).substring(0, 200), value: val })); } else if (modelField.type === 'object' || modelField.type === 'model') { throw new Error('Stackbit object and model field types can not be used with Contentful'); } return toDocumentField(value, localized, defaultLocale, { type: modelField.type }, (val) => ({ value: val })); } function toDocumentField(fieldValue, localized, defaultLocale, documentFieldBaseProps, documentFieldSpecificProps) { if (localized) { return { ...documentFieldBaseProps, localized: true, locales: lodash_1.default.mapValues(fieldValue, (val, locale) => ({ locale, ...documentFieldSpecificProps(val) })) }; } if (!fieldValue || !(defaultLocale in fieldValue)) { return null; // TODO: log user error // throw new Error(`Error in contentful-entries-converter, non localized field has no default locale`); } const defaultLocaleValue = fieldValue[defaultLocale]; return { ...documentFieldBaseProps, ...documentFieldSpecificProps(defaultLocaleValue) }; } function crossReferenceFieldPropsFromUrn(urn) { const match = urn.match(/crn:contentful:::content:spaces\/([^/]+)\/entries\/(.+)$/); const spaceId = match?.[1] ?? 'ERROR_PARSING_URN'; const entryId = match?.[2] ?? 'ERROR_PARSING_URN'; return { refId: entryId, refSrcType: 'contentful', refProjectId: spaceId }; } function flattenRichText(node) { if (lodash_1.default.get(node, 'nodeType') === 'text') { return lodash_1.default.get(node, 'value', ''); } const content = lodash_1.default.get(node, 'content'); if (content) { return lodash_1.default.reduce(content, (accum, node) => { return accum + flattenRichText(node); }, ''); } return ''; } function convertDocumentVersions({ entries, userMap, defaultLocale, getModelByName, projectUrl }) { return entries.map((entry) => { const document = convertEntry({ entry: entry.snapshot, getModelByName, userMap, defaultLocale, projectUrl }); if (!document) { throw new Error(`Could not convert entry ${entry.sys.id} to Document`); } return { id: entry.sys.id, documentId: entry.snapshot.sys.id, srcType: 'contentful', srcProjectId: entry.snapshot.sys.space.sys.id, createdAt: entry.sys.createdAt, createdBy: getUserEmailById(entry.sys?.createdBy?.sys?.id, userMap), document }; }); } //# sourceMappingURL=contentful-entries-converter.js.map