import { type WidgetContent, EmptyContent } from "@prismicio/types-internal/lib/content" import type { StaticWidget } from "@prismicio/types-internal/lib/customtypes" import { compact } from "fp-ts/lib/Array" import * as O from "fp-ts/lib/Option" import type { Renderer } from "./models" import type { Field } from "./models/fetch" import { renderIfNoFetchOrFetchDefined } from "./utils" export type WithObjectRenderers< R extends Renderer, D extends StaticWidget = R extends Renderer ? U : never, C extends WidgetContent = R extends Renderer ? U : never, F extends Field | undefined = R extends Renderer ? U : never, > = R & { renderV1Object(content: Array<[string, C]> | Record, fetch: Record | undefined): object renderV2Object( content: Array<[string, C]> | Record, fetch: Record | undefined, fieldsDef: Record, renderIfUndefined: { withFetch: "default" | "filter" }, renderIfNull: { default: "default" | "render" | "filter" withFetch: "default" | "filter" withFetchField: "default" | "render" | "filter" }, ): Record renderMocksObject( content: Array<[string, C]> | Record, fieldsDef: Record, ): Record } export function withObjectRenderers< D extends StaticWidget, C extends WidgetContent, F extends Field | undefined = undefined, >(renderer: Renderer): WithObjectRenderers> { return { ...renderer, renderV1Object(content, fetch) { const c = content instanceof Array ? content : Object.entries(content) return Object.fromEntries( compact( c.map(([key, widget]) => { if (EmptyContent.is(widget)) { return O.none } return renderIfNoFetchOrFetchDefined(fetch, key, (f) => O.some([key, renderer.renderV1(widget, f)])) }), ), ) }, renderV2Object(content, fetch, fieldsDef, renderIfUndefined, renderIfNull) { const itemByKey: Record = content instanceof Array ? Object.fromEntries(content) : content const result: Record = {} Object.entries(fieldsDef).forEach(([key, def]) => { const item = itemByKey[key] if (item === undefined) { if ((fetch && key in fetch) || !fetch) { result[key] = renderer.renderDefault(def) } else if (fetch) { if (renderIfUndefined.withFetch === "default") { result[key] = renderer.renderDefault(def) } } } else if (EmptyContent.is(item)) { if (fetch && key in fetch) { if (renderIfNull.withFetchField === "default") { result[key] = renderer.renderDefault(def) } else if (renderIfNull.withFetchField === "render") { result[key] = renderer.renderV2(def, item, fetch[key]) } } else if (fetch) { if (renderIfNull.withFetch === "default") { result[key] = renderer.renderDefault(def) } } else { if (renderIfNull.default === "default") { result[key] = renderer.renderDefault(def) } else if (renderIfNull.default === "render") { result[key] = renderer.renderV2(def, item) } } } else { const renderedO = renderIfNoFetchOrFetchDefined(fetch, key, (f) => O.some(renderer.renderV2(def, item, f))) if (O.isSome(renderedO)) { result[key] = renderedO.value } } }) return result }, renderMocksObject(content, fieldsDef) { const itemByKey: Record = content instanceof Array ? Object.fromEntries(content) : content const result: Record = {} Object.entries(fieldsDef).forEach(([name, def]) => { const key = name const item = itemByKey[key] if (item === undefined) { result[key] = renderer.renderDefault(def) } else if (EmptyContent.is(item)) { result[key] = renderer.renderDefault(def) } else { const renderedO = renderIfNoFetchOrFetchDefined(undefined, key, () => O.some(renderer.renderMocks(def, item))) if (O.isSome(renderedO)) { result[key] = renderedO.value } } }) return result }, } }