@prismicio/types-internal
Version:
Prismic types for Custom Types and Prismic Data
185 lines (169 loc) • 5.08 kB
text/typescript
import { either } from "fp-ts"
import { isLeft } from "fp-ts/lib/Either"
import { pipe } from "fp-ts/lib/function"
import * as t from "io-ts"
import type { StaticSlice } from "../../../customtypes"
import { nullable } from "../../../validators/function"
import {
getFieldCtx,
LegacyContentCtx,
WithTypes,
} from "../../LegacyContentCtx"
import type { TraverseSliceContentFn } from "../../utils"
import { isGroupContent } from "../GroupContent"
import { isNestableContent } from "../nestable"
import { repeatableContentWithDefaultNestableContentValues } from "../withDefaultValues"
import {
compositeSliceContentWithDefaultValues,
sharedSliceContentWithDefaultValues,
SliceContent,
SliceLegacy,
} from "./Slice"
import {
CompositeSliceContent,
isCompositeSliceContent,
migrateCompositeSlice,
} from "./Slice/CompositeSliceContent"
import {
isSharedSliceContent,
SharedSliceContent,
} from "./Slice/SharedSliceContent"
import {
isSimpleSliceContent,
migrateSimpleSlice,
SimpleSliceContent,
} from "./Slice/SimpleSliceContent"
export const SliceItemContent = t.type({
key: t.string,
name: t.string,
maybeLabel: t.union([t.string, t.undefined]),
widget: SliceContent,
})
export type SliceItemContent = t.TypeOf<typeof SliceItemContent>
export type SharedSliceItemContent = Omit<SliceItemContent, "widget"> & {
widget: SharedSliceContent
}
export function isSharedSliceItemContent(
item: SliceItemContent,
): item is SharedSliceItemContent {
return isSharedSliceContent(item.widget)
}
export type CompositeSliceItemContent = Omit<SliceItemContent, "widget"> & {
widget: CompositeSliceContent
}
export function isCompositeSliceItemContent(
item: SliceItemContent,
): item is CompositeSliceItemContent {
return isCompositeSliceContent(item.widget)
}
export type SimpleSliceItemContent = Omit<SliceItemContent, "widget"> & {
widget: SimpleSliceContent
}
export function isSimpleSliceItemContent(
item: SliceItemContent,
): item is SimpleSliceItemContent {
return isGroupContent(item.widget) || isNestableContent(item.widget)
}
export function sliceItemContentWithDefaultValues(
customType: StaticSlice,
content: SliceContent,
): SliceContent {
if (customType.type === "SharedSlice" && isSharedSliceContent(content)) {
return sharedSliceContentWithDefaultValues(customType, content)
}
if (customType.type === "Slice" && isCompositeSliceContent(content)) {
return compositeSliceContentWithDefaultValues(customType, content)
}
if (
customType.type === "Group" &&
isGroupContent(content) &&
customType.config?.fields
) {
const updateGroupFieldValue =
repeatableContentWithDefaultNestableContentValues(
customType.config.fields,
content.value,
)
return {
...content,
value: updateGroupFieldValue,
}
}
// Note - SimpleSliceContent can also be a NestableContent, but in that case we have no default value to fill, because the content is already there.
// There is no key specified in custom type that does not have a value provided like in other cases.
return content
}
const itemLegacyReader = t.exact(
t.intersection([
t.type({
key: t.string,
value: t.unknown,
}),
t.partial({
label: nullable(t.string),
}),
]),
)
export type SliceItemLegacy = t.TypeOf<typeof itemLegacyReader>
export const SlicesItemLegacy = (ctx: LegacyContentCtx) => {
return new t.Type<SliceItemContent, WithTypes<SliceItemLegacy>, unknown>(
"SlicesItemLegacy",
(u): u is SliceItemContent => (
isSharedSliceContent((u as SliceItemContent).widget),
isCompositeSliceContent((u as SliceItemContent).widget),
isSimpleSliceContent((u as SliceItemContent).widget)
),
(sliceItem, context) => {
return pipe(
itemLegacyReader.decode(sliceItem),
either.chain((parsedSlice) => {
const stopIdx = parsedSlice.key.indexOf("$")
const sliceName = parsedSlice.key.substring(
0,
stopIdx > 0 ? stopIdx : undefined,
)
const itemCtx = getFieldCtx({
fieldKey: sliceName,
contentKey: parsedSlice.key,
ctx,
})
const item = SliceLegacy(itemCtx).decode(parsedSlice.value)
if (!item || isLeft(item)) return t.failure(sliceItem, context)
return t.success({
key: parsedSlice.key,
name: sliceName,
maybeLabel: parsedSlice.label ?? undefined,
widget: item.right,
})
}),
)
},
(sItem: SliceItemContent) => {
const itemCtx = getFieldCtx({
fieldKey: sItem.name,
contentKey: sItem.key,
ctx,
})
const result = SliceLegacy(itemCtx).encode(sItem.widget)
return {
content: {
key: sItem.key,
label: sItem.maybeLabel,
value: result?.content || {},
},
types: result?.types || {},
keys: result?.keys || {},
}
},
)
}
export const migrateSliceItem: TraverseSliceContentFn = ({
model,
content,
}) => {
if (isCompositeSliceItemContent(content) && model?.type === "SharedSlice")
return migrateCompositeSlice(model, content)
if (isSimpleSliceItemContent(content) && model?.type === "SharedSlice")
return migrateSimpleSlice(model, content)
return content
}