UNPKG

@npio/internals

Version:

A free visual website editor, powered with your own SolidJS components.

262 lines (226 loc) 6.09 kB
import { NitroFont, NitroFontFace, NitroPreset } from "@prisma/client"; import { isServer } from "solid-js/web"; import type { Page, PageRevision } from "../server"; import { Element, FontFaceFile, State } from "../types"; import type { Settings } from "./server/settings"; const track = function (value: any) { if (isServer || value == null) return value; const type = typeof value; if (type !== "object") return value; if (Array.isArray(value)) { value.forEach(track); return value; } Object.values(value).forEach(track); return value; }; type OmitDates<T> = Omit<T, "createdAt" | "updatedAt">; type NormalizableFonts = (OmitDates<NitroFont> & { faces: OmitDates<NitroFontFace>[]; })[]; export const normalizeFonts = function ({ settings, fonts, }: { settings?: Settings; fonts: NormalizableFonts; }) { let defaultId: string | undefined; const normalizedFonts = fonts.map(function (font) { const normalizedFaces = font.faces.map(function (face) { const normalizedFiles = (JSON.parse(face.files) as FontFaceFile[]).sort( function (fileA, fileB) { const sortA = fontTypesSorting[fileA.format] || 1; const sortB = fontTypesSorting[fileB.format] || 1; return sortB - sortA; }, ); return { id: face.id, preload: face.preload, weight: face.weight!, style: face.style, files: normalizedFiles, }; }); if (settings?.defaultFont.value === font.id) { defaultId = font.publicId; } return { ...font, fallbacks: font.fallback.split(","), driver: font.driver || undefined, faces: normalizedFaces, }; }); return { defaultFont: defaultId, fonts: normalizedFonts, } satisfies Partial<State>; }; export const normalizePreset = (preset: NitroPreset) => { return { id: preset.id, name: preset.name, blueprint: preset.blueprint, data: JSON.parse(preset.data), }; }; export const normalizePresets = function ({ presets, }: { presets: NitroPreset[]; }) { return presets.reduce( (acc, preset) => { acc[preset.id] = normalizePreset(preset); return acc; }, {} as State["presets"], ); }; export const normalizeProjectSettings = function ({ settings, }: { settings: Settings; }) { return { backgroundColor: track(settings.pageBackgroundColor.value), metaImage: settings.metaImage.value?.mediaIds[0], favicon: settings.favicon.value?.mediaIds[0], }; }; export const normalizeSettings = function ({ settings, pageRevision, }: { pageRevision: PageRevision; settings: Settings; }) { return { notFound: settings.notFoundPage.value === pageRevision.pageId, metaDescription: settings.metaDescription.value, colors: track(settings.colors.value) || {}, richtext: { textColors: track(settings.rteTextColors.value) || {}, }, blueprintDefaults: { project: track(settings.blueprintDefaults.value) || {}, // TODO: Remove page blueprint defaults //page: JSON.parse(pageRevision.blueprintDefaults), }, ...normalizeProjectSettings({ settings }), } satisfies Partial<State>; }; export const normalizePage = function ({ pageRevision, page, passiveBlueprints = {}, }: { pageRevision: PageRevision; page?: Page; passiveBlueprints?: Record<string, boolean>; }) { const blueprintIds: Record<string, boolean> = {}; const dynamicElements: string[] = []; const elements = Object.values(pageRevision.elements).reduce( (acc, el) => { let dynamic = false; if (el.blueprintId) { blueprintIds[el.blueprintId] = false; if (!passiveBlueprints[el.blueprintId]) { dynamicElements.push(el.id); dynamic = true; } } // TODO: Implement non-dynamic layouts if (el.layoutId) { dynamicElements.push(el.id); dynamic = true; } const { createdAt, updatedAt, ...rest } = el; acc[el.id] = { ...rest, dynamic, }; return acc; }, {} as Record<string, Element>, ); const setIds: Record<string, boolean> = {}; const setDynamic = function (id: string) { if (setIds[id]) { return; } setIds[id] = true; const element = elements[id]; elements[id].dynamic = true; if (element.blueprintId) { blueprintIds[element.blueprintId] = true; } if (element.parentSlotId == null) return; const parentElement = pageRevision.slots[element.parentSlotId].parentElementId; if (parentElement == null) return; setDynamic(parentElement); }; for (const id of dynamicElements) { setDynamic(id); } return { title: pageRevision.title, urlPath: pageRevision.urlPath, includeInFeed: page?.includeInFeed, slots: pageRevision.slots, layoutSlots: page?.layoutSlots ?? {}, elements, blueprintIds, }; }; export const createIdMap = <T extends { id: string }, M = undefined>( list: T[], map?: (v: T) => M, ) => { const result: Record<string, M extends undefined ? T : M> = {}; for (const item of list) { result[item.id] = (map ? map(item) : item) as any; } return result; }; export const normalizeState = function ({ pageRevision, page, fonts, presets, settings, passiveBlueprints = {}, }: { pageRevision: PageRevision; page?: Page; settings: Settings; presets: NitroPreset[]; fonts: NormalizableFonts; passiveBlueprints?: Record<string, boolean>; }): State { const settingsData = normalizeSettings({ pageRevision, settings, }); const pageData = normalizePage({ page, pageRevision, passiveBlueprints }); const presetData = normalizePresets({ presets }); const fontData = normalizeFonts({ fonts, settings }); const result = { cacheKey: 0, presets: presetData, ...pageData, ...fontData, ...settingsData, } satisfies State; return result; }; export const fontTypesSorting: Record<string, number> = { woff2: 4, woff: 3, opentype: 2, truetype: 1, };