UNPKG

gatsby-source-prismic

Version:

Gatsby source plugin for building websites using Prismic as a data source

251 lines (245 loc) 6.76 kB
import * as gatsby from "gatsby"; import * as prismicT from "@prismicio/types"; import * as RTE from "fp-ts/ReaderTaskEither"; import * as R from "fp-ts/Record"; import * as A from "fp-ts/Array"; import * as ReadonlyA from "fp-ts/ReadonlyArray"; import * as E from "fp-ts/Either"; import * as O from "fp-ts/Option"; import { pipe, flow, identity } from "fp-ts/function"; import { buildObjectType } from "../lib/buildObjectType"; import { buildSchemaRecordType } from "../lib/buildSchemaRecordType"; import { buildUnionType } from "../lib/buildUnionType"; import { getTypeName } from "../lib/getTypeName"; import { listTypeName } from "../lib/listTypeName"; import { createType } from "../lib/createType"; import { createTypes } from "../lib/createTypes"; import { requiredTypeName } from "../lib/requiredTypeName"; import { Dependencies, FieldConfigCreator, UnknownRecord } from "../types"; /** * Builds a GraphQL field configuration object for a Slice zone's Slice. Both * `non-repeat` and `repeat` schemas will be converted to GraphQL field * configuration objects. The resulting type can be created using Gatsby's * `createTypes` action. * * This function registers a typepath for the field. * * @param path - Path to the Slice zone. * @param model - Schema definition for the Slice. * * @returns GraphQL object type. */ const buildSliceType = ( path: string[], model: prismicT.CustomTypeModelSlice, ): RTE.ReaderTaskEither<Dependencies, Error, gatsby.GatsbyGraphQLObjectType> => pipe( RTE.ask<Dependencies>(), RTE.chain((deps) => pipe( {} as Record< "primary" | "items", RTE.ReaderTaskEither< Dependencies, never, gatsby.GatsbyGraphQLObjectType > >, R.isEmpty(model["non-repeat"]) ? identity : R.upsertAt( "primary", buildSchemaRecordType([...path, "primary"], model["non-repeat"]), ), R.isEmpty(model.repeat) ? identity : R.upsertAt( "items", buildSchemaRecordType([...path, "items"], model.repeat, [ ...path, "item", ]), ), R.sequence(RTE.ApplicativeSeq), RTE.chainFirstW( flow( R.collect((_, type) => type), createTypes, ), ), RTE.map( R.mapWithIndex((field, type) => field === "items" ? pipe( type, getTypeName, requiredTypeName, listTypeName, requiredTypeName, ) : pipe(type, getTypeName, requiredTypeName), ), ), RTE.chainW((fields) => buildObjectType({ name: deps.nodeHelpers.createTypeName(path), fields: { ...fields, id: { type: "ID!", resolve: (source: UnknownRecord): string => deps.nodeHelpers.createNodeId([ ...path, deps.createContentDigest(source), ]), }, slice_type: "String!", slice_label: "String", }, interfaces: [deps.globalNodeHelpers.createTypeName("SliceType")], extensions: { infer: false }, }), ), ), ), ); /** * Builds GraphQL types for Slice Custom Type fields. The resulting types can be * created using Gatsby's `createTypes` action. * * @param path - Path to the fields. * @param choices - Record of Slice choices mapping a Slice API ID to its schema * definition. * * @returns List of GraphQL types for the provided Slice schemas. */ const buildSliceTypes = ( path: string[], choices: prismicT.CustomTypeModelSliceZoneField["config"]["choices"], ): RTE.ReaderTaskEither<Dependencies, Error, string[]> => pipe( RTE.ask<Dependencies>(), RTE.bindW("sliceModels", () => pipe( choices, R.filter( (slice): slice is prismicT.CustomTypeModelSlice => slice.type === prismicT.CustomTypeModelSliceType.Slice, ), RTE.right, ), ), RTE.bindW("sliceTypeNames", (scope) => pipe( scope.sliceModels, R.mapWithIndex((sliceName, sliceModel) => buildSliceType([...path, sliceName], sliceModel), ), R.sequence(RTE.ApplicativeSeq), RTE.map(R.collect((_, type) => type)), RTE.chainFirstW(createTypes), RTE.map(A.map(getTypeName)), ), ), RTE.bindW("sharedSliceModels", () => pipe( choices, R.filter( (slice): slice is prismicT.CustomTypeModelSharedSlice => slice.type === prismicT.CustomTypeModelSliceType.SharedSlice, ), RTE.right, ), ), RTE.bindW("sharedSliceTypesNames", (scope) => pipe( scope.sharedSliceModels, R.keys, A.map((sharedSliceId) => pipe( scope.pluginOptions.sharedSliceModels, A.findFirst( (sharedSliceModel) => sharedSliceModel.id === sharedSliceId, ), E.fromOption( () => new Error( `Could not find a Shared Slice model for a Shared Slice named "${sharedSliceId}"`, ), ), ), ), A.sequence(E.Applicative), RTE.fromEither, RTE.map( A.map((sharedSliceModel) => pipe( sharedSliceModel.variations, ReadonlyA.map((variation) => scope.nodeHelpers.createTypeName([ sharedSliceModel.id, variation.id, ]), ), ), ), ), RTE.map(ReadonlyA.flatten), RTE.map(ReadonlyA.toArray), ), ), RTE.map((scope) => [ ...scope.sliceTypeNames, ...scope.sharedSliceTypesNames, ]), ); /** * Builds a GraphQL field configuration object for a Slices Custom Type field * (also known as a Slice zone). The resulting configuration object can be used * in a GraphQL type. * * This function registers a typepath for the field. * * @param path - Path to the field. * @param schema - Schema definition for the field. * * @returns GraphQL field configuration object. */ export const buildSlicesFieldConfig: FieldConfigCreator< prismicT.CustomTypeModelSliceZoneField > = (path, schema) => pipe( RTE.ask<Dependencies>(), RTE.chain((deps) => pipe( buildSliceTypes(path, schema.config.choices), RTE.chainW((types) => buildUnionType({ name: deps.nodeHelpers.createTypeName([...path, "SlicesType"]), types, resolveType: (source: prismicT.Slice | prismicT.SharedSlice) => pipe( source, O.fromPredicate( (source): source is prismicT.SharedSlice => "variation" in source, ), O.map((source) => deps.nodeHelpers.createTypeName([ source.slice_type, source.variation, ]), ), O.getOrElse(() => deps.nodeHelpers.createTypeName([...path, source.slice_type]), ), ), }), ), RTE.chainFirstW(createType), RTE.map( flow(getTypeName, requiredTypeName, listTypeName, requiredTypeName), ), ), ), );