UNPKG

@firecms/core

Version:

Awesome Firebase/Firestore-based headless open-source CMS

182 lines (166 loc) 7.3 kB
import { EntityCollection, MapProperty, ModifyCollectionProps, PropertiesOrBuilders, Property, PropertyOrBuilder } from "../types"; import { mergeDeep } from "./objects"; import { sortProperties } from "./collections"; import { isPropertyBuilder } from "./entities"; function applyModifyFunction(modifyCollection: ((props: ModifyCollectionProps) => (EntityCollection | void)) | undefined, collection: EntityCollection, parentPaths: string[]) { if (modifyCollection) { const modified = modifyCollection({ collection, parentPaths }); const resCollection = modified ?? collection; if (resCollection.subcollections) { resCollection.subcollections = resCollection.subcollections.map((subcollection) => { return applyModifyFunction(modifyCollection, subcollection, [...parentPaths, collection.path]); }); } return resCollection; } else { return collection; } } /** * */ export function joinCollectionLists(targetCollections: EntityCollection[], sourceCollections: EntityCollection[] | undefined, parentPaths: string[] = [], modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void): EntityCollection[] { // merge collections that are in both lists const updatedCollections = (sourceCollections ?? []) .map((sourceCol) => { const targetCol = targetCollections?.find((collection) => { return collection.id === sourceCol.id; // return collection.path === codedCollection.path || collection.id && codedCollection.id; }); if (!targetCol) { return applyModifyFunction(modifyCollection, sourceCol, parentPaths); } else { return mergeCollection(targetCol, sourceCol, parentPaths, modifyCollection); } }); const sourceCollectionIds = updatedCollections.map(c => c.id); // fetched collections that are not in the source collections const resultStoredCollections = targetCollections .filter((col) => { return !sourceCollectionIds.includes(col.id); }) .map((col) => { if (modifyCollection) { return applyModifyFunction(modifyCollection, col, parentPaths); } else { return col; } }); return [...updatedCollections, ...resultStoredCollections]; } /** * */ export function mergeCollection(target: EntityCollection, source: EntityCollection, parentPaths: string[] = [], modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void ): EntityCollection { const subcollectionsMerged = joinCollectionLists( target?.subcollections ?? [], source?.subcollections ?? [], [...parentPaths, target.path], modifyCollection ); const propertiesMerged: PropertiesOrBuilders = { ...target.properties } as PropertiesOrBuilders; Object.keys(source.properties).forEach((key) => { const property = target.properties[key] as Property; if (property) propertiesMerged[key] = mergePropertyOrBuilder(property, source.properties[key] as PropertyOrBuilder); else propertiesMerged[key] = source.properties[key] as PropertyOrBuilder; }); const mergedCollection = mergeDeep(target, source, true); const targetPropertiesOrder = getCollectionKeys(target); const sourcePropertiesOrder = getCollectionKeys(source); const mergedPropertiesOrder = [...new Set([...sourcePropertiesOrder, ...targetPropertiesOrder])]; const mergedEntityViews = [...new Set([...(target.entityViews ?? []), ...(source.entityViews ?? [])])]; const mergedEntityActions = [...new Set([...(target.entityActions ?? []), ...(source.entityActions ?? [])])]; let resultCollection: EntityCollection = { ...mergedCollection, subcollections: subcollectionsMerged, properties: sortProperties(propertiesMerged, mergedPropertiesOrder), propertiesOrder: mergedPropertiesOrder, entityViews: mergedEntityViews, entityActions: mergedEntityActions, }; if (modifyCollection) { const modifiedCollection = modifyCollection({ collection: resultCollection, parentPaths }); if (modifiedCollection) resultCollection = modifiedCollection; } // @ts-ignore resultCollection["merged"] = true; return resultCollection } function mergePropertyOrBuilder(target: PropertyOrBuilder, source: PropertyOrBuilder): PropertyOrBuilder { if (!source) return target; if (!target) return source; if (isPropertyBuilder(source)) { return source; } else if (isPropertyBuilder(target)) { return target; } else { const mergedProperty = mergeDeep(target, source); const targetEditable = target.editable === undefined ? true : Boolean(target.editable); const sourceEditable = source.editable === undefined ? true : Boolean(source.editable); if (source.dataType === "map" && source.properties) { const targetProperties = ("properties" in target ? target.properties : {}) as PropertiesOrBuilders; const sourceProperties = ("properties" in source ? source.properties : {}) as PropertiesOrBuilders; const targetPropertiesOrder = "propertiesOrder" in target && target.propertiesOrder ? target.propertiesOrder : Object.keys(targetProperties); const sourcePropertiesOrder = "propertiesOrder" in source && source.propertiesOrder ? source.propertiesOrder : ("properties" in source ? Object.keys(source.properties) : []); const mergedPropertiesOrder = [...new Set([...targetPropertiesOrder, ...sourcePropertiesOrder])]; const mergedProperties: PropertiesOrBuilders = { ...targetProperties } as PropertiesOrBuilders; Object.keys(source.properties).forEach((key) => { const property = targetProperties[key] as Property; if (property) mergedProperties[key] = mergePropertyOrBuilder(property, sourceProperties[key] as PropertyOrBuilder); }); return { ...mergedProperty, editable: targetEditable && sourceEditable, properties: mergedProperties, propertiesOrder: mergedPropertiesOrder } as MapProperty; } return { ...mergedProperty, editable: targetEditable && sourceEditable }; } } function getCollectionKeys(collection: EntityCollection) { if (collection.propertiesOrder && collection.propertiesOrder.length > 0) { const propertiesOrder = collection.propertiesOrder; if (collection.additionalFields) { collection.additionalFields.forEach((field) => { if (!propertiesOrder.includes(field.key)) { propertiesOrder.push(field.key); } }); } return propertiesOrder; } return [ ...Object.keys(collection.properties), ...(collection.additionalFields ?? [])?.map(f => f.key) ]; }