Files
static-cms/packages/core/src/lib/util/nested.util.ts
Daniel Lautzenheiser 6be11c749e fix: tests (#710)
2023-04-18 23:05:15 -04:00

180 lines
4.9 KiB
TypeScript

import trim from 'lodash/trim';
import { basename, dirname, extname, join } from 'path';
import { sanitizeSlug } from '../urlHelper';
import { selectEntryCollectionTitle, selectFolderEntryExtension } from './collection.util';
import { isEmpty, isNotEmpty } from './string.util';
import { stringTemplate } from '../widgets';
import type { BaseField, Collection, Config, Entry } from '@staticcms/core/interface';
const { addFileTemplateFields } = stringTemplate;
interface BaseTreeNodeData {
title: string | undefined;
path: string;
isDir: boolean;
isRoot: boolean;
expanded?: boolean;
}
export type SingleTreeNodeData = BaseTreeNodeData | (Entry & BaseTreeNodeData);
export type TreeNodeData = SingleTreeNodeData & {
children: TreeNodeData[];
};
export function selectCustomPath(
entry: Entry,
collection: Collection,
rootSlug: string | undefined,
config: Config | undefined,
): string | undefined {
if (!('nested' in collection) || !collection.nested?.path) {
return undefined;
}
const indexFile = collection.nested.path.index_file;
const extension = selectFolderEntryExtension(collection);
const slug = entry.meta?.path ?? getNestedSlug(collection, entry, rootSlug, config);
const customPath = join(collection.folder, slug, `${indexFile}.${extension}`);
return customPath;
}
export function customPathFromSlug(collection: Collection, slug: string): string {
if (!('nested' in collection) || !collection.nested) {
return '';
}
if (collection.nested.path) {
if ('nested' in collection && collection.nested?.path) {
return slug.replace(new RegExp(`/${collection.nested.path.index_file}$`, 'g'), '');
}
}
return slug;
}
export function slugFromCustomPath(collection: Collection, customPath: string): string {
if (!('folder' in collection)) {
return '';
}
const folderPath = collection.folder;
const entryPath = customPath.toLowerCase().replace(folderPath.toLowerCase(), '');
const slug = join(dirname(trim(entryPath, '/')), basename(entryPath, extname(customPath)));
return slug;
}
export function getNestedSlug(
collection: Collection,
entry: Entry,
slug: string | undefined,
config: Config | undefined,
) {
if ('nested' in collection && collection.nested?.path) {
if (isNotEmpty(entry.slug)) {
return entry.slug.replace(new RegExp(`/${collection.nested.path.index_file}$`, 'g'), '');
} else if (slug) {
let summarySlug = selectEntryCollectionTitle(collection, entry);
if (isEmpty(summarySlug)) {
summarySlug = `new-${collection.label_singular ?? collection.label}`;
}
return `${customPathFromSlug(collection, slug)}/${sanitizeSlug(
summarySlug.toLowerCase(),
config?.slug,
)}`;
}
}
return '';
}
export function getTreeData<EF extends BaseField>(
collection: Collection<EF>,
entries: Entry[],
): TreeNodeData[] {
const collectionFolder = 'folder' in collection ? collection.folder : '';
const rootFolder = '/';
const entriesObj = entries.map(e => ({ ...e, path: e.path.slice(collectionFolder.length) }));
const dirs = entriesObj.reduce((acc, entry) => {
let dir: string | undefined = dirname(entry.path);
while (dir && !acc[dir] && dir !== rootFolder) {
const parts: string[] = dir.split('/');
acc[dir] = parts.pop();
dir = parts.length ? parts.join('/') : undefined;
}
return acc;
}, {} as Record<string, string | undefined>);
if ('nested' in collection && collection.nested?.summary) {
collection = {
...collection,
summary: collection.nested.summary,
};
} else {
collection = {
...collection,
};
delete collection.summary;
}
const flatData = [
{
title: collection.label,
path: rootFolder,
isDir: true,
isRoot: true,
},
...Object.entries(dirs).map(([key, value]) => ({
title: value,
path: key,
isDir: true,
isRoot: false,
})),
...entriesObj.map((e, index) => {
let entry = entries[index];
entry = {
...entry,
data: addFileTemplateFields(entry.path, entry.data as Record<string, string>),
};
const title = selectEntryCollectionTitle(collection, entry);
return {
...e,
title,
isDir: false,
isRoot: false,
};
}),
];
const parentsToChildren = flatData.reduce((acc, node) => {
const parent = node.path === rootFolder ? '' : dirname(node.path);
if (acc[parent]) {
acc[parent].push(node);
} else {
acc[parent] = [node];
}
return acc;
}, {} as Record<string, BaseTreeNodeData[]>);
function reducer(acc: TreeNodeData[], value: BaseTreeNodeData) {
const node = value;
let children: TreeNodeData[] = [];
if (parentsToChildren[node.path]) {
children = parentsToChildren[node.path].reduce(reducer, []);
}
acc.push({ ...node, children });
return acc;
}
const treeData = parentsToChildren[''].reduce(reducer, []);
return treeData;
}