import { bundleMDX } from 'mdx-bundler' ; import type * as U from 'unified' ; import type * as M from 'mdast' ; import type * as H from 'hast' ; import { remarkCodeBlocksShiki } from '@kentcdodds/md-temp' ; import remarkEmbedder from '@remark-embedder/core' ; import type { TransformerInfo } from '@remark-embedder/core' ; import oembedTransformer from '@remark-embedder/transformer-oembed' ; import calculateReadingTime from 'reading-time' ; import type TPQueue from 'p-queue' ; import type { GitHubFile } from '~/types' ; import * as twitter from './twitter.server' ; function handleEmbedderError ( { url } ) { return `<p>Error embedding <a href="${ url }">${ url }</a>.` ; } type GottenHTML = string | null ; function handleEmbedderHtml ( html , info ) { if ( ! html ) return null ; const url = new URL ( info . url ) ; if ( /youtu\.?be/ . test ( url . hostname ) ) { return makeEmbed ( html , 'youtube' ) ; } if ( url . hostname . includes ( 'codesandbox.io' ) ) { return makeEmbed ( html , 'codesandbox' , '80%' ) ; } return html ; } function makeEmbed ( html , type , heightRatio = '56.25%' ) { return `
  <div class="embed" data-embed-type="${ type }">
    <div style="padding-bottom: ${ heightRatio }">
      ${ html }
    </div>
  </div>
` ; } const cloudinaryUrlRegex = /^https?:\/\/res\.cloudinary\.com\/(?<cloudName>.+?)\/image\/upload(\/(?<transforms>(?!v\d+).+?))?(\/(?<version>v\d+))?\/(?<publicId>.+$)/ ; function optimizeCloudinaryImages ( ) { return async function transformer ( tree ) { const { visit } = await import ( 'unist-util-visit' ) ; visit ( tree , 'image' , function visitor ( node ) { if ( ! node . url ) { console . error ( 'image without url?' , node ) ; return ; } const urlString = String ( node . url ) ; const match = urlString . match ( cloudinaryUrlRegex ) ; const groups = match ?. groups ; if ( groups ) { const { cloudName , transforms , version , publicId } = groups as { cloudName : string ; transforms ? : string ; version ? : string ; publicId : string ; } ; if ( transforms ) return ; const defaultTransforms = 'f_auto,q_auto,dpr_2.0' ; node . url = [ `https://res.cloudinary.com/${ cloudName }/image/upload` , defaultTransforms , version , publicId , ] . filter ( Boolean ) . join ( '/' ) ; } } ) ; } ; } const twitterTransformer = { shouldTransform : twitter . isTwitterUrl , getHTML : twitter . getTweetEmbedHTML , } ; const eggheadTransformer = { shouldTransform : ( url ) => { const { host , pathname } = new URL ( url ) ; return ( host === 'egghead.io' && pathname . includes ( '/lessons/' ) && ! pathname . includes ( '/embed' ) ) ; } , getHTML : ( url ) => { const { host , pathname , searchParams } = new URL ( url ) ; if ( ! searchParams . has ( 'preload' ) ) { searchParams . set ( 'preload' , 'false' ) ; } if ( ! searchParams . has ( 'af' ) ) { searchParams . set ( 'af' , '5236ad' ) ; } const iframeSrc = `https://${ host }${ pathname }/embed?${ searchParams . toString ( ) }` ; return makeEmbed ( `<iframe src="${ iframeSrc }" allowfullscreen></iframe>` , 'egghead' ) ; } , } ; function autoAffiliates ( ) { return async function affiliateTransformer ( tree ) { const { visit } = await import ( 'unist-util-visit' ) ; visit ( tree , 'link' , function visitor ( linkNode ) { if ( linkNode . url . includes ( 'amazon.com' ) ) { const amazonUrl = new URL ( linkNode . url ) ; if ( ! amazonUrl . searchParams . has ( 'tag' ) ) { amazonUrl . searchParams . set ( 'tag' , 'kentcdodds-20' ) ; linkNode . url = amazonUrl . toString ( ) ; } } if ( linkNode . url . includes ( 'egghead.io' ) ) { const eggheadUrl = new URL ( linkNode . url ) ; if ( ! eggheadUrl . searchParams . has ( 'af' ) ) { eggheadUrl . searchParams . set ( 'af' , '5236ad' ) ; linkNode . url = eggheadUrl . toString ( ) ; } } } ) ; } ; } function removePreContainerDivs ( ) { return async function preContainerDivsTransformer ( tree ) { const { visit } = await import ( 'unist-util-visit' ) ; visit ( tree , { type : 'element' , tagName : 'pre' } , function visitor ( node , index , parent ) { if ( parent ?. type !== 'element' ) return ; if ( parent . tagName !== 'div' ) return ; if ( parent . children . length !== 1 && index === 0 ) return ; Object . assign ( parent , node ) ; } ) ; } ; } const remarkPlugins = [ remarkCodeBlocksShiki , optimizeCloudinaryImages , [ remarkEmbedder , { handleError : handleEmbedderError , handleHTML : handleEmbedderHtml , transformers : [ twitterTransformer , eggheadTransformer , oembedTransformer ] , } , ] , autoAffiliates , ] ; const rehypePlugins = [ removePreContainerDivs ] ; async function compileMdx < FrontmatterType extends Record < string , unknown > > ( slug , githubFiles ) { const { default : remarkAutolinkHeadings } = await import ( 'remark-autolink-headings' ) ; const { default : remarkSlug } = await import ( 'remark-slug' ) ; const { default : gfm } = await import ( 'remark-gfm' ) ; const indexRegex = new RegExp ( `${ slug }\\/index.mdx?$` ) ; const indexFile = githubFiles . find ( ( { path } ) => indexRegex . test ( path ) ) ; if ( ! indexFile ) return null ; const rootDir = indexFile . path . replace ( /index.mdx?$/ , '' ) ; const relativeFiles = githubFiles . map ( ( { path , content } ) => ( { path : path . replace ( rootDir , './' ) , content , } ) ) ; const files = arrayToObj ( relativeFiles , { keyName : 'path' , valueName : 'content' , } ) ; try { const { frontmatter , code } = await bundleMDX ( { source : indexFile . content , files , xdmOptions ( options ) { options . remarkPlugins = [ ... ( options . remarkPlugins ?? [ ] ) , remarkSlug , [ remarkAutolinkHeadings , { behavior : 'wrap' } ] , gfm , ... remarkPlugins , ] ; options . rehypePlugins = [ ... ( options . rehypePlugins ?? [ ] ) , ... rehypePlugins , ] ; return options ; } , } ) ; const readTime = calculateReadingTime ( indexFile . content ) ; return { code , readTime , frontmatter : frontmatter as FrontmatterType , } ; } catch ( error ) { console . error ( `Compilation error for slug: ` , slug ) ; throw error ; } } function arrayToObj < ItemType extends Record < string , unknown > > ( array , { keyName , valueName } ) { const obj = { } ; for ( const item of array ) { const key = item [ keyName ] ; if ( typeof key !== 'string' ) { throw new Error ( `${ keyName } of item must be a string` ) ; } const value = item [ valueName ] ; obj [ key ] = value ; } return obj ; } let _queue = null ; async function getQueue ( ) { const { default : PQueue } = await import ( 'p-queue' ) ; if ( _queue ) return _queue ; _queue = new PQueue ( { concurrency : 1 } ) ; return _queue ; } async function queuedCompileMdx < FrontmatterType extends Record < string , unknown > > ( ... args ) { const queue = await getQueue ( ) ; const result = await queue . add ( ( ) => compileMdx < FrontmatterType > ( ... args ) ) ; return result ; } export { queuedCompileMdx as compileMdx } ;