UNPKG

@prismicio/types-internal

Version:
185 lines (168 loc) 4.54 kB
import * as t from "io-ts" import type { OnFieldFn } from "../../../_internal/utils" import { WidgetKey } from "../../../common" import { StringOrNull } from "../../../validators" import { Group, NestedGroup, traverseNestedGroup } from "../Group" import type { NestableWidget } from "../nestable" import { CompositeSlice, traverseCompositeSlice } from "./CompositeSlice" import { LegacySlice } from "./LegacySlice" import { SharedSlice, traverseSharedSlice } from "./SharedSlice" import { SharedSliceRef } from "./SharedSliceRef" export const LegacySlicesFieldType = "Choice" export const SlicesFieldType = "Slices" export const SlicesLabels = t.union([ t.record( t.string, t.readonlyArray( t.exact( t.intersection([ t.type({ name: t.string, }), t.partial({ display: t.string, }), ]), ), ), ), t.null, ]) export type SlicesLabels = t.TypeOf<typeof SlicesLabels> export function slicesConfigReader<T extends SharedSlice | SharedSliceRef>( codec: t.Type<T, unknown>, ) { return t.exact( t.partial({ label: StringOrNull, labels: SlicesLabels, choices: t.record( WidgetKey, t.union([LegacySlice, CompositeSlice, codec]), ), }), ) } export const StaticSlicesConfig = slicesConfigReader(SharedSlice) export type StaticSlicesConfig = t.TypeOf<typeof StaticSlicesConfig> export const DynamicSlicesConfig = slicesConfigReader(SharedSliceRef) export type DynamicSlicesConfig = t.TypeOf<typeof DynamicSlicesConfig> const SlicesConfig = { toStatic( config: DynamicSlicesConfig, sharedSlices: Map<string, SharedSlice>, ): StaticSlicesConfig { const choices: { [key: string]: LegacySlice | CompositeSlice | SharedSlice } = Object.entries(config.choices || {}).reduce((acc, [ref, slice]) => { if (slice.type === "SharedSlice") { const sharedSlice = sharedSlices.get(ref) if (sharedSlice) return { ...acc, [ref]: sharedSlice } else return acc } else { return { ...acc, [ref]: slice } } }, {}) return { ...config, choices } as StaticSlicesConfig }, } export function slicesReader< T extends StaticSlicesConfig | DynamicSlicesConfig, >(codec: t.Type<T, unknown>) { return t.exact( t.intersection([ t.type({ type: t.keyof({ [SlicesFieldType]: null, [LegacySlicesFieldType]: null, }), }), t.partial({ fieldset: StringOrNull, config: codec, }), ]), ) } export const StaticSlices = slicesReader(StaticSlicesConfig) export type StaticSlices = t.TypeOf<typeof StaticSlices> export const DynamicSlices = slicesReader(DynamicSlicesConfig) export type DynamicSlices = t.TypeOf<typeof DynamicSlices> export const Slices = { toStatic( slices: DynamicSlices, sharedSlices: Map<string, SharedSlice>, ): StaticSlices { if (!slices.config) return slices as StaticSlices else { return { ...slices, config: SlicesConfig.toStatic(slices.config, sharedSlices), } } }, } export function traverseSlices<T extends DynamicSlices | StaticSlices>(args: { path: ReadonlyArray<string> slices: T onField: OnFieldFn<NestableWidget | Group | NestedGroup> }): T { const { path: prevPath, slices, onField } = args if (!slices.config?.choices) return slices let choices: Record<string, typeof slices.config.choices[string]> | undefined for (const [key, prevModel] of Object.entries(slices.config.choices)) { const path = [...prevPath, key] let model switch (prevModel.type) { case "Slice": model = traverseCompositeSlice({ path, slice: prevModel, onField: onField as OnFieldFn<NestableWidget>, }) break case "SharedSlice": if ("variations" in prevModel) model = traverseSharedSlice({ path, slice: prevModel as SharedSlice, onField, }) else model = prevModel break // Group and other fields are technically possible because of legacy slices. case "Group": model = onField({ path, key, field: traverseNestedGroup({ path, group: prevModel, onField: onField as OnFieldFn<NestableWidget>, }), }) break default: model = onField({ path, key, field: prevModel, }) break } if (model !== prevModel) { if (!choices) choices = { ...slices.config.choices } choices[key] = model as typeof slices.config.choices[string] } } // returns the traversed model instead of a new one if it didn't change return choices ? { ...slices, config: { ...slices.config, choices, }, } : slices }