import _ from 'lodash'; import { Config } from '@stackbit/sdk'; import * as CSITypes from '@stackbit/types'; import { ContentStoreTypes } from '../'; import { getContentSourceId } from '../content-store-utils'; export async function getSanitizedTreeViews({ configDelegate, stackbitConfig, contentSourceDataById, logger }: { configDelegate: CSITypes.ConfigDelegate; stackbitConfig: Config | null; contentSourceDataById: Record; logger: CSITypes.Logger; }): Promise { const treeViews = (await stackbitConfig?.treeViews?.(configDelegate)) ?? []; const mapTreeViews = (treeViews: CSITypes.TreeViewNode[]): CSITypes.TreeViewNode[] => treeViews .map((treeView) => { if ('document' in treeView && treeView.document) { const contentSourceId = getContentSourceId(treeView.document.srcType, treeView.document.srcProjectId); const document = contentSourceDataById[contentSourceId]?.documentMap[treeView.document.id]; // explicit check because developers can skip TS check and just not define required properties of document object if (document) { treeView = { ...treeView, stableId: treeView.stableId ?? document.srcObjectId, label: treeView.label ?? document.getPreview({ delegate: configDelegate }).previewTitle, document: { srcType: treeView.document.srcType, srcProjectId: treeView.document.srcProjectId, id: treeView.document.id } }; } else { logger.warn( `One of required properties is missing in tree view document, document skipped from tree view, srcType: ${treeView.document?.srcType}, srcProjectId: ${treeView.document?.srcProjectId}, id: ${treeView.document?.id}.` ); // don't early return because treeView.children can be defined properly if (treeView.children) { treeView = _.omit(treeView, 'document') as CSITypes.TreeViewBaseNode; } else { return undefined; } } } if ('children' in treeView && treeView.children) { // required check because of // 1. typecast during omit in previous if // 2. developer can ignore TS check and just not define stableId and label if (treeView.stableId && treeView.label) { treeView = { ...treeView, children: mapTreeViews(treeView.children) }; } else { logger.warn('one of required properties (stableId or label) is missing in tree view, children are skipped from tree view'); return undefined; } } return treeView; }) .filter(Boolean) as CSITypes.TreeViewNode[]; return mapTreeViews(treeViews); } export function removeHiddenTreeViews({ treeViews, getDocumentForUser }: { treeViews: CSITypes.TreeViewNode[]; getDocumentForUser: (opts: { srcDocumentId: string; srcType: string; srcProjectId: string }) => ContentStoreTypes.Document | undefined; }) { const mapTreeViews = (treeViews: CSITypes.TreeViewNode[]): CSITypes.TreeViewNode[] => { return treeViews.map((treeView) => { let resultTreeView = treeView; if ('document' in treeView && treeView.document) { const document = getDocumentForUser({ srcDocumentId: treeView.document.id, srcType: treeView.document.srcType, srcProjectId: treeView.document.srcProjectId }); if (!document) { throw new Error(`Could not found a document with id ${treeView.document.id} when iterating tree views`); } if (document.hidden) { resultTreeView = _.omit(treeView, ['document']) as CSITypes.TreeViewBaseNode; } } if ('children' in treeView && treeView.children) { if (treeView.stableId && treeView.label) { resultTreeView = { ...resultTreeView, children: mapTreeViews(treeView.children) }; } } return resultTreeView; }); }; return mapTreeViews(treeViews); }