UNPKG

@nitrogenbuilder/client-core

Version:

Nitrogen Builder Core Client

201 lines (177 loc) 4.52 kB
import type { Category, ComponentManifest, ComponentManifestCategory, ComponentManifestChildren, ComponentManifestEntry, ComponentManifestGroup, ComponentManifestOption, ComponentManifestProp, ComponentSettings, Prop, } from '@nitrogenbuilder/types'; export interface ComponentManifestSource { name: string; scope?: string; settings: ComponentSettings; } type Serializable = | null | boolean | number | string | Serializable[] | { [key: string]: Serializable }; const PROP_COPY_KEYS: Array<keyof ComponentManifestProp> = [ 'label', 'default', 'responsive', 'divider', 'clearable', 'conditions', 'allowOther', 'options', 'accept', 'ctrlType', 'useUnit', 'step', 'sources', 'dialogTitle', 'collection', 'initialLimitDefault', 'totalLimitDefault', 'loadMoreLimitDefault', 'useLoadMoreDefault', 'usePaginationDefault', 'config', 'hasToolbar', 'defaultLinkState', 'layout', 'layoutMode', 'startLabel', 'endLabel', 'min', 'max', ]; function sanitizeValue(value: unknown): Serializable | undefined { if (value === undefined) return undefined; if ( value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ) { return value; } if (Array.isArray(value)) { return value .map((entry) => sanitizeValue(entry)) .filter((entry) => entry !== undefined) as Serializable[]; } if (typeof value === 'function') { return undefined; } if (typeof value === 'object') { const next: Record<string, Serializable> = {}; for (const [key, entry] of Object.entries(value as Record<string, unknown>)) { const serialized = sanitizeValue(entry); if (serialized !== undefined) { next[key] = serialized; } } return next; } return undefined; } function sanitizeOptions(options: unknown): ComponentManifestOption[] | undefined { const serialized = sanitizeValue(options); if (!Array.isArray(serialized)) return undefined; return serialized as ComponentManifestOption[]; } function normalizeProp(prop: Prop): ComponentManifestProp { const next: ComponentManifestProp = { type: prop.type, }; for (const key of PROP_COPY_KEYS) { const value = sanitizeValue((prop as Record<string, unknown>)[key]); if (value !== undefined) { if (key === 'options') { next.options = sanitizeOptions(value); continue; } (next as unknown as Record<string, unknown>)[key] = value; } } if ('props' in prop && prop.props) { next.props = normalizePropMap(prop.props); } if (prop.type === 'asset-folder' && 'files' in prop) { next.meta = { assetCount: Object.keys(prop.files || {}).length, }; } return next; } function normalizePropMap(props: Record<string, Prop>): Record<string, ComponentManifestProp> { return Object.fromEntries( Object.entries(props).map(([propKey, prop]) => [propKey, normalizeProp(prop)]), ); } function normalizeGroup(groupKey: string, group: Category['groups'][string]): ComponentManifestGroup { return { key: groupKey, label: group.label, props: normalizePropMap(group.props), }; } function normalizeCategory(categoryKey: string, category: Category): ComponentManifestCategory { return { key: categoryKey, label: category.label, groups: Object.fromEntries( Object.entries(category.groups).map(([groupKey, group]) => [ groupKey, normalizeGroup(groupKey, group), ]), ), }; } function normalizeChildren(children: unknown): ComponentManifestChildren | undefined { const serialized = sanitizeValue(children); if (serialized === undefined) return undefined; return serialized as ComponentManifestChildren; } export function createComponentManifestEntry({ name, scope, settings, }: ComponentManifestSource): ComponentManifestEntry { return { name, scope, description: settings.description, sidebarCategory: settings.options?.category, children: normalizeChildren(settings.options?.children), categories: Object.fromEntries( Object.entries(settings.categories).map(([categoryKey, category]) => [ categoryKey, normalizeCategory(categoryKey, category), ]), ), }; } export function createComponentManifest( components: ComponentManifestSource[], options?: { source?: string; generatedAt?: string; } ): ComponentManifest { return { components: [...components] .sort((a, b) => a.name.localeCompare(b.name)) .map((component) => createComponentManifestEntry(component)), generatedAt: options?.generatedAt ?? new Date().toISOString(), source: options?.source, }; }