UNPKG

gatsby-source-prismic

Version:

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

184 lines (179 loc) 5.63 kB
import * as gatsby from "gatsby"; import * as gqlc from "graphql-compose"; import * as prismicT from "@prismicio/types"; import * as prismicH from "@prismicio/helpers"; import * as RTE from "fp-ts/ReaderTaskEither"; import * as R from "fp-ts/Record"; import * as S from "fp-ts/Semigroup"; import * as A from "fp-ts/Array"; import * as struct from "fp-ts/struct"; import { pipe } from "fp-ts/function"; import { Dependencies, PrismicAPIDocumentNode } from "../types"; import { PREVIEWABLE_NODE_ID_FIELD } from "../constants"; import { getTypeName } from "./getTypeName"; import { buildObjectType } from "./buildObjectType"; import { createType } from "./createType"; import { buildFieldConfigMap } from "./buildFieldConfigMap"; import { listTypeName } from "./listTypeName"; import { requiredTypeName } from "./requiredTypeName"; /** * Returns all fields from a Prismic Custom Type schema definition. It * effectively gathers all fields for each tab into one object. * * @param schema - Schema definition for a Custom Type. * * @returns Record of fields for the Custom Type. */ const collectFields = ( schema: prismicT.CustomTypeModel, ): Record<string, prismicT.CustomTypeModelField> => pipe( schema.json, R.collect((_, value) => value), S.concatAll(struct.getAssignSemigroup<prismicT.CustomTypeModelTab>())({}), ); /** * Builds a record of GraphQL field configuration objects containing a Custom * Type's `data` fields. The resulting record can be used within a GraphQL type * for the Custom Type. * * @param customTypeName - API ID of the Custom Type. * @param fields - Record mapping Custom Type field API IDs to their schema definitions. * * @returns Record of GraphQL field configuration objects containing * `data`-related fields. */ const buildDataFieldConfigMap = ( customTypeName: string, fields: Record<string, prismicT.CustomTypeModelField>, ): RTE.ReaderTaskEither< Dependencies, never, | gqlc.ObjectTypeComposerFieldConfigMapDefinition< PrismicAPIDocumentNode, unknown > | undefined > => pipe( RTE.ask<Dependencies>(), RTE.filterOrElse( () => !R.isEmpty(fields), () => new Error("No data fields in schema"), ), RTE.bindW("fieldConfigMap", () => buildFieldConfigMap([customTypeName, "data"], fields), ), RTE.chainW((scope) => buildObjectType({ name: scope.nodeHelpers.createTypeName([customTypeName, "DataType"]), fields: scope.fieldConfigMap, }), ), RTE.chainFirstW(createType), RTE.map(getTypeName), RTE.map((typeName) => ({ data: pipe(typeName, requiredTypeName), dataRaw: { type: "JSON!", resolve: (source: PrismicAPIDocumentNode) => source.data, }, })), // We will be spreading the return value of this function into the // document's config map, so we can return undefined as an empty value. // Leaving it as an E.left would have stopped the custom type from being // created. RTE.orElse(() => RTE.right( undefined as | gqlc.ObjectTypeComposerFieldConfigMapDefinition< PrismicAPIDocumentNode, unknown > | undefined, ), ), ); /** * Creates a GraphQL type for a Custom Type. This function also registers a * root-level typepath for the Custom Type. * * @param name - API ID of the Custom Type * @param customType - Schema definition for the Custom Type. * * @returns GraphQL type for the Custom Type. */ export const createCustomType = ( customType: prismicT.CustomTypeModel, ): RTE.ReaderTaskEither<Dependencies, Error, gatsby.GatsbyGraphQLObjectType> => pipe( RTE.ask<Dependencies>(), RTE.bind("fields", () => RTE.right(collectFields(customType))), RTE.bind("partitionedFields", (scope) => pipe( scope.fields, R.partition( (field) => field.type === prismicT.CustomTypeModelFieldType.UID, ), (partitionedFields) => RTE.right(partitionedFields), ), ), RTE.bind("rootFieldConfigMap", (scope) => pipe( scope.partitionedFields.right, R.collect((k, a) => [k, a] as [string, prismicT.CustomTypeModelField]), A.map( ([k, a]) => (a.type === prismicT.CustomTypeModelFieldType.UID ? ["uid", a] : [k, a]) as [string, prismicT.CustomTypeModelField], ), R.fromFoldable(S.last<prismicT.CustomTypeModelField>(), A.Foldable), (fields) => buildFieldConfigMap([customType.id], fields), ), ), RTE.bindW("dataFieldConfigMap", (scope) => buildDataFieldConfigMap(customType.id, scope.partitionedFields.left), ), RTE.chainW((scope) => buildObjectType({ name: scope.nodeHelpers.createTypeName(customType.id), fields: { ...scope.rootFieldConfigMap, ...scope.dataFieldConfigMap, [scope.nodeHelpers.createFieldName("id") as "id"]: "ID!", alternate_languages: pipe( scope.nodeHelpers.createTypeName("AlternateLanguageType"), requiredTypeName, listTypeName, requiredTypeName, ), first_publication_date: { type: "Date!", extensions: { dateformat: {} }, }, href: "String!", lang: "String!", last_publication_date: { type: "Date!", extensions: { dateformat: {} }, }, tags: "[String!]!", type: "String!", url: { type: "String", resolve: (source: PrismicAPIDocumentNode) => prismicH.asLink(source, scope.pluginOptions.linkResolver), }, [PREVIEWABLE_NODE_ID_FIELD]: { type: "ID!", resolve: (source: PrismicAPIDocumentNode) => source[scope.nodeHelpers.createFieldName("id")], }, }, interfaces: ["Node"], extensions: { infer: false }, }), ), RTE.chainFirstW(createType), );