@prismicio/types-internal
Version:
Prismic types for Custom Types and Prismic Data
165 lines (141 loc) • 3.83 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 {
ContentPath,
TraverseWidgetContentFn,
} from "../../../_internal/utils"
import type { Link, NestableWidget } from "../../../customtypes"
import type { LegacyContentCtx, WithTypes } from "../../LegacyContentCtx"
import { hasContentType } from "../../utils"
import {
isLinkContent,
LinkContent,
LinkContentLegacy,
LinkContentType,
} from "./LinkContent"
const RepeatableContentType = "RepeatableContent" as const
export const RepeatableContent = t.strict({
__TYPE__: t.literal(RepeatableContentType),
type: t.literal("Link"),
value: t.array(LinkContent),
})
export type RepeatableContent = t.TypeOf<typeof RepeatableContent>
export const isRepeatableContent = (u: unknown): u is RepeatableContent =>
hasContentType(u) && u.__TYPE__ === RepeatableContentType
export const RepeatableLegacy = (
ctx: LegacyContentCtx,
fieldType: "Link",
): t.Type<RepeatableContent, WithTypes<Array<unknown>>, unknown> => {
return new t.Type<RepeatableContent, WithTypes<Array<unknown>>, unknown>(
"RepeatableLegacy",
isRepeatableContent,
(items) => {
const parsed = pipe(
t.array(t.unknown).decode(items),
either.map((items) => {
const parsedItems = items.reduce<Array<LinkContent>>((acc, item) => {
let result
switch (fieldType) {
case "Link":
result = LinkContentLegacy(ctx).decode(item)
break
}
if (!result) return acc
if (isLeft(result)) return acc
return [...acc, result.right]
}, [])
return {
value: parsedItems,
type: fieldType,
__TYPE__: "RepeatableContent" as const,
}
}),
)
return parsed
},
(r: RepeatableContent) => {
const res = t.array(LinkContent).encode(r.value)
const encodedItems = res.reduce<Array<WithTypes<unknown>>>(
(acc, item) => {
let encoded
switch (item.__TYPE__) {
case LinkContentType:
encoded = LinkContentLegacy(ctx).encode(item)
break
}
if (!encoded) return acc
return [...acc, encoded]
},
[],
)
return {
content: encodedItems.map((encodedItem) => encodedItem.content),
types: { [ctx.keyOfType]: `Repeatable.${fieldType}` },
keys: encodedItems.reduce<Record<string, string>>(
(acc, item) => ({ ...acc, ...item.keys }),
{},
),
}
},
)
}
export type RepeatableCustomType = Link
export function traverseRepeatableContent({
path,
key,
apiId,
model,
content,
}: {
path: ContentPath
key: string
apiId: string
content: RepeatableContent
model?: NestableWidget | undefined
}) {
return (
transform: TraverseWidgetContentFn,
): RepeatableContent | undefined => {
const items = content.value.reduce<Array<LinkContent>>(
(acc, fieldContent, index) => {
const itemPath = path.concat([
{ key: index.toString(), type: "Widget" },
])
// Content inside repeatable can't be repeatable.
const newModel =
model?.type === "Link" && model.config
? {
...model,
config: { ...model.config, repeat: false },
}
: model
const transformedField = transform({
path: itemPath,
key: key,
apiId: apiId,
model: newModel,
content: fieldContent,
})
// Can happen if the transform function returns undefined to filter out a field
if (!transformedField) return acc
// If the transformed field is not a link content, we don't include it
if (!isLinkContent(transformedField)) return acc
return acc.concat(transformedField)
},
[],
)
return transform({
path,
key,
apiId,
model,
content: {
__TYPE__: content.__TYPE__,
type: content.type,
value: items,
},
})
}
}