import type { PosixFilePath } from '@contentlayer/utils' ; import { filePathJoin , unknownToPosixFilePath } from '@contentlayer/utils' ; import * as utils from '@contentlayer/utils' ; import type { E , HasClock , HasConsole } from '@contentlayer/utils/effect' ; import { Chunk , flow , OT , pipe , S , T } from '@contentlayer/utils/effect' ; import { fs } from '@contentlayer/utils/node' ; import { camelCase } from 'camel-case' ; import type { PackageJson } from 'type-fest' ; import { ArtifactsDir } from '../ArtifactsDir.js' ; import type { HasCwd } from '../cwd.js' ; import type { DataCache } from '../DataCache.js' ; import type { SourceProvideSchemaError } from '../errors.js' ; import type { SourceFetchDataError } from '../index.js' ; import type { PluginOptions , SourcePlugin , SourcePluginType } from '../plugin.js' ; import type { DocumentTypeDef , SchemaDef } from '../schema/index.js' ; import { autogeneratedNote } from './common.js' ; import { renderTypes } from './generate-types.js' ; /**
 * Used to track which files already have been written.
 * Gets re-initialized per `generateDotpkg` invocation therefore only "works" during dev mode.
 */ type FilePath = string ; type DocumentHash = string ; type WrittenFilesCache = Record < FilePath , DocumentHash > ; export type GenerationOptions = { sourcePluginType : SourcePluginType ; options : PluginOptions ; } ; type GenerateDotpkgError = fs . WriteFileError | fs . JsonStringifyError | fs . MkdirError | fs . RmError | SourceProvideSchemaError | SourceFetchDataError ; export type GenerateInfo = { documentCount : number ; } ; export const logGenerateInfo = ( info ) : T . Effect < HasConsole , never , void > => T . log ( `Generated ${ info . documentCount } documents in node_modules/.contentlayer` ) ; export const generateDotpkg = ( { source , verbose , } ) : T . Effect < OT . HasTracer & HasClock & HasCwd & HasConsole , GenerateDotpkgError , GenerateInfo > => pipe ( generateDotpkgStream ( { source , verbose } ) , S . take ( 1 ) , S . runCollect , T . map ( Chunk . unsafeHead ) , T . rightOrFail , OT . withSpan ( '@contentlayer/core/generation:generateDotpkg' , { attributes : { verbose } } ) ) ; export const generateDotpkgStream = ( { source , verbose , } ) : S . Stream < OT . HasTracer & HasClock & HasCwd & HasConsole , never , E . Either < GenerateDotpkgError , GenerateInfo > > => { const writtenFilesCache = { } ; const generationOptions = { sourcePluginType : source . type , options : source . options } ; const resolveParams = pipe ( T . structPar ( { schemaDef : source . provideSchema , targetPath : ArtifactsDir . mkdir , } ) , T . either ) ; return pipe ( S . fromEffect ( resolveParams ) , S . chainMapEitherRight ( ( { schemaDef , targetPath } ) => pipe ( source . fetchData ( { schemaDef , verbose } ) , S . mapEffectEitherRight ( ( cache ) => pipe ( writeFilesForCache ( { schemaDef , targetPath , cache , generationOptions , writtenFilesCache } ) , T . eitherMap ( ( ) => ( { documentCount : Object . keys ( cache . cacheItemsMap ) . length } ) ) ) ) ) ) ) ; } ; const writeFilesForCache = ( { cache , schemaDef , targetPath , generationOptions , writtenFilesCache , } ) : T . Effect < OT . HasTracer , never , E . Either < fs . WriteFileError | fs . MkdirError | fs . RmError | fs . JsonStringifyError , void > > => pipe ( T . gen ( function * ( $ ) { const withPrefix = ( ... path_ ) => filePathJoin ( targetPath , ... path_ . map ( unknownToPosixFilePath ) ) ; if ( process . env [ 'CL_DEBUG' ] ) { yield * $ ( fs . mkdirp ( withPrefix ( 'cache' ) ) ) ; yield * $ ( T . collectAllPar ( [ fs . writeFileJson ( { filePath : withPrefix ( 'cache' , 'schema.json' ) , content : schemaDef as any } ) , fs . writeFileJson ( { filePath : withPrefix ( 'cache' , 'data-cache.json' ) , content : cache } ) , ] ) ) ; } const allCacheItems = Object . values ( cache . cacheItemsMap ) ; const allDocuments = allCacheItems . map ( ( _ ) => _ . document ) ; const documentDefs = Object . values ( schemaDef . documentTypeDefMap ) ; const typeNameField = generationOptions . options . fieldOptions . typeFieldName ; const dataBarrelFiles = documentDefs . map ( ( docDef ) => ( { content : makeDataExportFile ( { docDef , documentIds : allDocuments . filter ( ( _ ) => _ [ typeNameField ] === docDef . name ) . map ( ( _ ) => _ . _id ) , } ) , filePath : withPrefix ( 'data' , `${ getDataVariableName ( { docDef } ) }.mjs` ) , } ) ) ; const dataJsonFiles = allCacheItems . map ( ( { document , documentHash } ) => ( { content : JSON . stringify ( document , null , 2 ) , filePath : withPrefix ( 'data' , document [ typeNameField ] , `${ idToFileName ( document . _id ) }.json` ) , documentHash , } ) ) ; const dataDirPaths = documentDefs . map ( ( _ ) => withPrefix ( 'data' , _ . name ) ) ; yield * $ ( T . forEachPar_ ( [ withPrefix ( 'types' ) , ... dataDirPaths ] , fs . mkdirp ) ) ; const writeFile = writeFileWithWrittenFilesCache ( { writtenFilesCache } ) ; yield * $ ( T . collectAllPar ( [ writeFile ( { filePath : withPrefix ( 'package.json' ) , content : makePackageJson ( schemaDef . hash ) } ) , writeFile ( { filePath : withPrefix ( 'types' , 'index.d.ts' ) , content : renderTypes ( { schemaDef , generationOptions } ) , rmBeforeWrite : true , } ) , writeFile ( { filePath : withPrefix ( 'types' , 'index.mjs' ) , content : makeHelperTypes ( ) } ) , writeFile ( { filePath : withPrefix ( 'data' , 'index.d.ts' ) , content : makeDataTypes ( { schemaDef } ) , rmBeforeWrite : true , } ) , writeFile ( { filePath : withPrefix ( 'data' , 'index.mjs' ) , content : makeIndexJs ( { schemaDef } ) } ) , ... dataBarrelFiles . map ( writeFile ) , ... dataJsonFiles . map ( writeFile ) , ] ) ) ; } ) , OT . withSpan ( '@contentlayer/core/generation/generate-dotpkg:writeFilesForCache' , { attributes : { targetPath , cacheKeys : Object . keys ( cache . cacheItemsMap ) , } , } ) , T . either ) ; const makePackageJson = ( schemaHash ) : string => { const packageJson = { name : 'dot-contentlayer' , description : 'This package is auto-generated by Contentlayer' , version : `0.0.0-${ schemaHash }` , exports : { './data' : { import : './data/index.mjs' , } , './types' : { import : './types/index.mjs' , } , } , typesVersions : { '*' : { data : [ './data' ] , types : [ './types' ] , } , } , } ; return JSON . stringify ( packageJson , null , 2 ) ; } ; /**
 * Remembers which files already have been written to disk.
 * If no `documentHash` was provided, the writes won't be cached. */ const writeFileWithWrittenFilesCache = ( { writtenFilesCache } ) => ( { filePath , content , documentHash , rmBeforeWrite = true , } ) => T . gen ( function * ( $ ) { const fileIsUpToDate = documentHash !== undefined && writtenFilesCache [ filePath ] === documentHash ; if ( ! rmBeforeWrite && fileIsUpToDate ) { return ; } if ( rmBeforeWrite ) { yield * $ ( fs . rm ( filePath , { force : true } ) ) ; } yield * $ ( fs . writeFile ( filePath , content ) ) ; if ( documentHash ) { writtenFilesCache [ filePath ] = documentHash ; } } ) ; const makeDataExportFile = ( { docDef , documentIds } ) : string => { const dataVariableName = getDataVariableName ( { docDef } ) ; if ( docDef . isSingleton ) { const documentId = documentIds [ 0 ] ! ; return `\
// ${ autogeneratedNote }
export { default as ${ dataVariableName } } from './${ docDef . name }/${ idToFileName ( documentId ) }.json'
` ; } const makeVariableName = flow ( idToFileName , ( _ ) => camelCase ( _ , { stripRegexp : /[^A-Z0-9\_]/gi } ) ) ; const docImports = documentIds . map ( ( _ ) => `import ${ makeVariableName ( _ ) } from './${ docDef . name }/${ idToFileName ( _ ) }.json'` ) . join ( '\n' ) ; return `\
// ${ autogeneratedNote }

${ docImports }

export const ${ dataVariableName } = [${ documentIds . map ( ( _ ) => makeVariableName ( _ ) ) . join ( ', ' ) }]
` ; } ; const makeIndexJs = ( { schemaDef } ) : string => { const dataVariableNames = Object . values ( schemaDef . documentTypeDefMap ) . map ( ( docDef ) => [ docDef , getDataVariableName ( { docDef } ) ] as const ) ; const constReexports = dataVariableNames . map ( ( [ , dataVariableName ] ) => `export * from './${ dataVariableName }.mjs'` ) . join ( '\n' ) ; const constImportsForAllDocuments = dataVariableNames . map ( ( [ , dataVariableName ] ) => `import { ${ dataVariableName } } from './${ dataVariableName }.mjs'` ) . join ( '\n' ) ; const allDocuments = dataVariableNames . map ( ( [ docDef , dataVariableName ] ) => ( docDef . isSingleton ? dataVariableName : `...${ dataVariableName }` ) ) . join ( ', ' ) ; return `\
// ${ autogeneratedNote }

export { isType } from 'contentlayer/client'

${ constReexports }
${ constImportsForAllDocuments }

export const allDocuments = [${ allDocuments }]
` ; } ; const makeHelperTypes = ( ) : string => { return `\
// ${ autogeneratedNote }

export { isType } from 'contentlayer/client'
` ; } ; const makeDataTypes = ( { schemaDef } ) : string => { const dataConsts = Object . values ( schemaDef . documentTypeDefMap ) . map ( ( docDef ) => [ docDef , docDef . name , getDataVariableName ( { docDef } ) ] as const ) . map ( ( [ docDef , typeName , dataVariableName ] ) => `export declare const ${ dataVariableName }: ${ typeName }${ docDef . isSingleton ? '' : '[]' }` ) . join ( '\n' ) ; const documentTypeNames = Object . values ( schemaDef . documentTypeDefMap ) . map ( ( docDef ) => docDef . name ) . join ( ', ' ) ; return `\
// ${ autogeneratedNote }

import { ${ documentTypeNames }, DocumentTypes } from '../types'

${ dataConsts }

export declare const allDocuments: DocumentTypes[]

` ; } ; const getDataVariableName = ( { docDef } ) : string => { if ( docDef . isSingleton ) { return utils . lowercaseFirstChar ( utils . inflection . singularize ( docDef . name ) ) ; } else { return 'all' + utils . uppercaseFirstChar ( utils . inflection . pluralize ( docDef . name ) ) ; } } ; const idToFileName = ( id ) : string => leftPadWithUnderscoreIfStartsWithNumber ( id ) . replace ( /\//g , '__' ) ; const leftPadWithUnderscoreIfStartsWithNumber = ( str ) : string => { if ( /^[0-9]/ . test ( str ) ) { return '_' + str ; } return str ; } ;