import type { UnexpectedMarkdownError , UnexpectedMDXError } from '@contentlayer/core' ; import * as core from '@contentlayer/core' ; import type { PosixFilePath } from '@contentlayer/utils' ; import * as utils from '@contentlayer/utils' ; import type { HasConsole , OT } from '@contentlayer/utils/effect' ; import { pipe , T } from '@contentlayer/utils/effect' ; import dateFnsTz from 'date-fns-tz' ; import * as path from 'path' ; import { FetchDataError } from '../errors/index.js' ; import type { DocumentBodyType } from '../schema/defs/index.js' ; import type { RawDocumentData } from '../types.js' ; import type { RawContent , RawContentMarkdown , RawContentMDX } from './types.js' ; export const makeDocument = ( { rawContent , documentTypeDef , coreSchemaDef , relativeFilePath , contentDirPath , options , } ) : T . Effect < OT . HasTracer & HasConsole , FetchDataError . UnexpectedError | FetchDataError . NoSuchNestedDocumentTypeError , core . Document > => pipe ( T . gen ( function * ( $ ) { const { bodyFieldName , typeFieldName } = options . fieldOptions ; const body = utils . pattern . match ( rawContent ) . when ( rawContentHasBody , ( _ ) => _ . body ) . otherwise ( ( ) => undefined ) ; const rawData = { ... rawContent . fields , [ bodyFieldName ] : body } ; const docValues = yield * $ ( T . forEachParDict_ ( documentTypeDef . fieldDefs , { mapValue : ( fieldDef ) => getDataForFieldDef ( { fieldDef , rawFieldData : rawData [ fieldDef . name ] , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) , mapKey : ( fieldDef ) => T . succeed ( fieldDef . name ) , } ) ) ; const bodyType = utils . pattern . match ( rawContent . kind ) . with ( 'markdown' , ( ) => 'markdown' as const ) . with ( 'mdx' , ( ) => 'mdx' as const ) . otherwise ( ( ) => 'none' as const ) ; const _raw = { sourceFilePath : relativeFilePath , sourceFileName : path . basename ( relativeFilePath ) , sourceFileDir : path . dirname ( relativeFilePath ) , bodyType , flattenedPath : getFlattenedPath ( relativeFilePath ) , } ; const doc = { ... docValues , _id : relativeFilePath , _raw , [ typeFieldName ] : documentTypeDef . name , } ; return doc ; } ) , T . mapError ( ( error ) => error . _tag === 'NoSuchNestedDocumentTypeError' ? error : new FetchDataError . UnexpectedError ( { error , documentFilePath : relativeFilePath } ) ) ) ; type MakeDocumentInternalError = UnexpectedMarkdownError | UnexpectedMDXError | FetchDataError . NoSuchNestedDocumentTypeError ; const rawContentHasBody = ( _ ) : _ is RawContentMarkdown | RawContentMDX => 'body' in _ && _ . body !== undefined ; export const getFlattenedPath = ( relativeFilePath ) : string => relativeFilePath . split ( '.' ) . slice ( 0 , - 1 ) . join ( '.' ) . replace ( /\/?index$/ , '' ) ; const makeNestedDocument = ( { rawObjectData , fieldDefs , typeName , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) : T . Effect < OT . HasTracer & HasConsole , MakeDocumentInternalError , core . NestedDocument > => T . gen ( function * ( $ ) { const objValues = yield * $ ( T . forEachParDict_ ( fieldDefs , { mapValue : ( fieldDef ) => getDataForFieldDef ( { fieldDef , rawFieldData : rawObjectData [ fieldDef . name ] , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) , mapKey : ( fieldDef ) => T . succeed ( fieldDef . name ) , } ) ) ; const typeNameField = options . fieldOptions . typeFieldName ; const obj = { ... objValues , [ typeNameField ] : typeName , _raw : { } } ; return obj ; } ) ; const getDataForFieldDef = ( { fieldDef , rawFieldData , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) : T . Effect < OT . HasTracer & HasConsole , MakeDocumentInternalError , any > => T . gen ( function * ( $ ) { if ( rawFieldData === undefined && fieldDef . default ) { rawFieldData = fieldDef . default ; } if ( rawFieldData === undefined ) { if ( fieldDef . isRequired && ! fieldDef . isSystemField ) { console . error ( `Inconsistent data found: ${ JSON . stringify ( fieldDef ) }` ) ; } return undefined ; } switch ( fieldDef . type ) { case 'nested' : { const nestedTypeDef = coreSchemaDef . nestedTypeDefMap [ fieldDef . nestedTypeName ] ! ; return yield * $ ( makeNestedDocument ( { rawObjectData : rawFieldData , fieldDefs : nestedTypeDef . fieldDefs , typeName : nestedTypeDef . name , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ) ; } case 'nested_unnamed' : return yield * $ ( makeNestedDocument ( { rawObjectData : rawFieldData , fieldDefs : fieldDef . typeDef . fieldDefs , typeName : '__UNNAMED__' , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ) ; case 'nested_polymorphic' : { const typeName = rawFieldData [ fieldDef . typeField ] ; if ( ! fieldDef . nestedTypeNames . includes ( typeName ) ) { return yield * $ ( T . fail ( new FetchDataError . NoSuchNestedDocumentTypeError ( { documentTypeName : typeName , documentFilePath : relativeFilePath , fieldName : fieldDef . name , validNestedTypeNames : fieldDef . nestedTypeNames , } ) ) ) ; } const nestedTypeDef = coreSchemaDef . nestedTypeDefMap [ typeName ] ! ; return yield * $ ( makeNestedDocument ( { rawObjectData : rawFieldData , fieldDefs : nestedTypeDef . fieldDefs , typeName : nestedTypeDef . name , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ) ; } case 'reference' : case 'reference_polymorphic' : return rawFieldData ; case 'list_polymorphic' : case 'list' : return yield * $ ( T . forEachPar_ ( rawFieldData as any [ ] , ( rawItemData ) => getDataForListItem ( { rawItemData , fieldDef , coreSchemaDef , options , relativeFilePath , contentDirPath } ) ) ) ; case 'date' : let dateValue = new Date ( rawFieldData ) ; if ( options . date ?. timezone ) { dateValue = dateFnsTz . zonedTimeToUtc ( dateValue , options . date . timezone ) ; } return dateValue . toISOString ( ) ; case 'markdown' : const html = yield * $ ( core . markdownToHtml ( { mdString : rawFieldData , options : options ?. markdown } ) ) ; return < core . Markdown > { raw : rawFieldData , html } ; case 'mdx' : const code = yield * $ ( core . bundleMDX ( { mdxString : rawFieldData , options : options ?. mdx , contentDirPath } ) ) ; return < core . MDX > { raw : rawFieldData , code } ; case 'boolean' : case 'string' : case 'number' : case 'json' : case 'enum' : return rawFieldData ; default : utils . casesHandled ( fieldDef ) ; } } ) ; export const testOnly_getDataForFieldDef = getDataForFieldDef ; const getDataForListItem = ( { rawItemData , fieldDef , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) : T . Effect < OT . HasTracer & HasConsole , MakeDocumentInternalError , any > => { if ( typeof rawItemData === 'string' ) { return T . succeed ( rawItemData ) ; } if ( fieldDef . type === 'list_polymorphic' ) { const nestedTypeName = rawItemData [ fieldDef . typeField ] ; const nestedTypeDef = coreSchemaDef . nestedTypeDefMap [ nestedTypeName ] ; if ( nestedTypeDef === undefined ) { const validNestedTypeNames = fieldDef . of . filter ( ( _ ) : _ is core . ListFieldDefItem . ItemNested => _ . type === 'nested' ) . map ( ( _ ) => _ . nestedTypeName ) ; return T . fail ( new FetchDataError . NoSuchNestedDocumentTypeError ( { documentTypeName : nestedTypeName , documentFilePath : relativeFilePath , fieldName : fieldDef . name , validNestedTypeNames , } ) ) ; } return makeNestedDocument ( { rawObjectData : rawItemData , fieldDefs : nestedTypeDef . fieldDefs , typeName : nestedTypeDef . name , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ; } switch ( fieldDef . of . type ) { case 'nested' : const nestedTypeDef = coreSchemaDef . nestedTypeDefMap [ fieldDef . of . nestedTypeName ] ! ; return makeNestedDocument ( { rawObjectData : rawItemData , fieldDefs : nestedTypeDef . fieldDefs , typeName : nestedTypeDef . name , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ; case 'nested_unnamed' : return makeNestedDocument ( { rawObjectData : rawItemData , fieldDefs : fieldDef . of . typeDef . fieldDefs , typeName : '__UNNAMED__' , coreSchemaDef , options , relativeFilePath , contentDirPath , } ) ; case 'boolean' : case 'enum' : case 'reference' : case 'string' : return T . succeed ( rawItemData ) ; default : return utils . casesHandled ( fieldDef . of ) ; } } ;