import _ from 'lodash'; import * as CSITypes from '@stackbit/types'; import { omitByNil } from '@stackbit/utils'; import * as ContentStoreTypes from '../types'; export function mapStoreDocumentsToCSIDocumentsWithSource({ documents, csiDocumentMap }: { documents: ContentStoreTypes.Document[]; csiDocumentMap: Record; }): CSITypes.DocumentWithSource[] { return documents.map((document) => mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument: csiDocumentMap[document.srcObjectId]! }) ); } /** * This method converts documents stored in an internal ContentStoreTypes.Document * format to documents in a public CSITypes.DocumentWithSource format. These * documents are then passed to document hook and sitemap functions defined in * stackbit.config.ts. * * Most of the properties, including fields and their types, are taken from the * internal documents that contain changes made by mapDocuments, mapModels and * modelExtensions functions. * * The document's context is taken from the CSI Document because context is not * included in the internal documents. */ export function mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument }: { document: ContentStoreTypes.Document; csiDocument: CSITypes.Document; }): CSITypes.DocumentWithSource { return { type: document.type, id: document.srcObjectId, srcType: document.srcType, srcProjectId: document.srcProjectId, manageUrl: document.srcObjectUrl, modelName: document.srcModelName, status: document.status, createdAt: document.createdAt, createdBy: document.createdBy, updatedAt: document.updatedAt, updatedBy: document.updatedBy, locale: document.locale, context: csiDocument.context, hidden: document.hidden, fields: mapStoreFieldsToCSIFields(document.fields) }; } function mapStoreFieldsToCSIFields(documentFields: Record): Record { return _.reduce( documentFields, (accum: Record, documentField, fieldName) => { const csiField = mapStoreFieldToCSIField(documentField); if (csiField) { accum[fieldName] = csiField; } return accum; }, {} ); } export function mapStoreFieldToCSIField( documentField: ContentStoreTypes.DocumentFieldLocalizedForType ): CSITypes.DocumentFieldLocalizedForType | undefined; export function mapStoreFieldToCSIField( documentField: ContentStoreTypes.DocumentFieldNonLocalizedForType ): CSITypes.DocumentFieldNonLocalizedForType | undefined; export function mapStoreFieldToCSIField( documentField: ContentStoreTypes.DocumentFieldForType ): CSITypes.DocumentFieldForType | undefined; export function mapStoreFieldToCSIField(documentField: ContentStoreTypes.DocumentField): CSITypes.DocumentField | undefined { switch (documentField.type) { case 'string': case 'text': case 'html': case 'slug': case 'url': case 'color': case 'boolean': case 'number': case 'date': case 'datetime': case 'enum': case 'file': case 'json': case 'style': case 'markdown': case 'richText': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: documentField.type, localized: true, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, value: locale.value })) }; } if (typeof documentField.value === 'undefined' || ('isUnset' in documentField && documentField.isUnset)) { return; } return { type: documentField.type, value: documentField.value } as CSITypes.DocumentField; } case 'image': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return omitByNil({ type: 'image', localized: true, source: documentField.source, locales: _.mapValues(documentField.locales, (locale) => omitByNil({ locale: locale.locale, sourceData: locale.sourceData, ...(locale.sourceData ? null : { fields: mapStoreFieldsToCSIFields(locale.fields) }) }) ) }) as CSITypes.DocumentImageFieldLocalized; } if (documentField.isUnset) { return; } return omitByNil({ type: 'image', source: documentField.source, sourceData: documentField.sourceData, ...(documentField.sourceData ? null : { fields: mapStoreFieldsToCSIFields(documentField.fields) }) }) as CSITypes.DocumentImageFieldNonLocalized; } case 'object': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: 'object', localized: true, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, fields: mapStoreFieldsToCSIFields(locale.fields) })) }; } if (documentField.isUnset) { return; } return { type: 'object', fields: mapStoreFieldsToCSIFields(documentField.fields) }; } case 'model': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: 'model', localized: true, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, modelName: locale.srcModelName, fields: mapStoreFieldsToCSIFields(locale.fields) })) }; } if (documentField.isUnset) { return; } return { type: 'model', modelName: documentField.srcModelName, fields: mapStoreFieldsToCSIFields(documentField.fields) }; } case 'reference': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: 'reference', localized: true, refType: documentField.refType, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, refId: locale.refId })) }; } if (documentField.isUnset) { return; } return { type: 'reference', refType: documentField.refType, refId: documentField.refId }; } case 'cross-reference': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: 'json', localized: true, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, value: { refId: locale.refId, refSrcType: locale.refSrcType, srcProjectId: locale.refProjectId } })) }; } if (documentField.isUnset) { return; } return { type: 'json', value: { refId: documentField.refId, refSrcType: documentField.refSrcType, srcProjectId: documentField.refProjectId } }; } case 'list': { if (documentField.localized) { if (_.isEmpty(documentField.locales)) { return; } return { type: 'list', localized: true, locales: _.mapValues(documentField.locales, (locale) => ({ locale: locale.locale, items: locale.items.reduce((accum: CSITypes.DocumentListFieldItems[], item) => { const csiItem = mapStoreFieldToCSIField(item); // shouldn't happen because all list item fields are non-localized by definition, // but just in case and for the sake of typescript if (!csiItem) { return accum; } return accum.concat(csiItem); }, []) })) }; } return { type: 'list', items: documentField.items.reduce((accum: CSITypes.DocumentListFieldItems[], item) => { const csiItem = mapStoreFieldToCSIField(item); // shouldn't happen because all list item fields are non-localized by definition, // but just in case and for the sake of typescript if (!csiItem) { return accum; } return accum.concat(csiItem); }, []) }; } default: { const _exhaustiveCheck: never = documentField; return _exhaustiveCheck; } } }