import { Actions , NodePluginArgs } from 'gatsby' ; import { extractWithPath } from '@sanity/mutator' ; import { specifiedScalarTypes } from 'gatsby/graphql' ; import { set , startCase , camelCase , cloneDeep , upperFirst } from 'lodash' ; import { SanityDocument } from '../types/sanity' ; import { safeId , unprefixId } from './documentIds' ; import { TypeMap } from './remoteGraphQLSchema' ; import { SanityInputNode } from '../types/gatsby' ; const scalarTypeNames = specifiedScalarTypes . map ( ( def ) => def . name ) . concat ( [ 'JSON' , 'Date' ] ) ; const typePrefix = 'Sanity' ; export const RESTRICTED_NODE_FIELDS = [ 'id' , 'children' , 'parent' , 'fields' , 'internal' ] ; export interface ProcessingOptions { typeMap : TypeMap ; createNode : Actions [ 'createNode' ] ; createNodeId : NodePluginArgs [ 'createNodeId' ] ; createContentDigest : NodePluginArgs [ 'createContentDigest' ] ; createParentChildLink : Actions [ 'createParentChildLink' ] ; overlayDrafts : boolean ; } export function toGatsbyNode ( doc , options ) { const { createNodeId , createContentDigest , overlayDrafts } = options ; const rawAliases = getRawAliases ( doc , options ) ; const safe = prefixConflictingKeys ( doc ) ; const withRefs = rewriteNodeReferences ( safe , options ) ; return { ... withRefs , ... rawAliases , id : safeId ( overlayDrafts ? unprefixId ( doc . _id ) : doc . _id , createNodeId ) , children : [ ] , internal : { type : getTypeName ( doc . _type ) , contentDigest : createContentDigest ( JSON . stringify ( withRefs ) ) , } , } ; } export function getTypeName ( type ) { if ( ! type ) { return type ; } const typeName = startCase ( type ) ; if ( scalarTypeNames . includes ( typeName ) ) { return typeName ; } return `${ typePrefix }${ typeName . replace ( /\s+/g , '' ) . replace ( /^Sanity/ , '' ) }` ; } function prefixConflictingKeys ( obj ) { const initial = { _id : '' , _type : '' , _rev : '' , _createdAt : '' , _updatedAt : '' } ; return Object . keys ( obj ) . reduce ( ( target , key ) => { const targetKey = getConflictFreeFieldName ( key ) ; target [ targetKey ] = obj [ key ] ; return target ; } , initial ) ; } export function getConflictFreeFieldName ( fieldName ) { return RESTRICTED_NODE_FIELDS . includes ( fieldName ) ? `${ camelCase ( typePrefix ) }${ upperFirst ( fieldName ) }` : fieldName ; } function getRawAliases ( doc , options ) { const { typeMap } = options ; const typeName = getTypeName ( doc . _type ) ; const type = typeMap . objects [ typeName ] ; if ( ! type ) { return { } ; } const initial = { } ; return Object . keys ( type . fields ) . reduce ( ( acc , fieldName ) => { const field = type . fields [ fieldName ] ; const namedType = field . namedType . name . value ; if ( field . aliasFor ) { const aliasName = '_' + camelCase ( `raw_data_${ field . aliasFor }` ) ; acc [ aliasName ] = doc [ field . aliasFor ] ; return acc ; } if ( typeMap . scalars . includes ( namedType ) ) { return acc ; } const aliasName = '_' + camelCase ( `raw_data_${ fieldName }` ) ; acc [ aliasName ] = doc [ fieldName ] ; return acc ; } , initial ) ; } function rewriteNodeReferences ( doc , options ) { const { createNodeId } = options ; const refs = extractWithPath ( '..[_ref]' , doc ) ; if ( refs . length === 0 ) { return doc ; } const newDoc = cloneDeep ( doc ) ; refs . forEach ( ( match ) => { set ( newDoc , match . path , safeId ( match . value , createNodeId ) ) ; } ) ; return newDoc ; }