Mark Eibes
2023-06-24T10:38:01Z

GetMdxFiles.purs

| module BuildTime.GetMDXFiles where
|
| import Prelude
| import Node.FS.Aff (readTextFile, readdir)
| import Data.Foldable (for_)
| import Node.Glob.Basic (expandGlobsCwd)
| import BuildTime.MDXBundler (EsbuildOptions, Extension(Extension), RehypePlugin, RemarkPlugin, addLoader, addRehypePlugins, addRemarkPlugins, bundleMDX, setPlatform)
| import Node.Encoding (Encoding(..))
| import Debug (spy)
| import Effect.Aff (launchAff_)
| import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2)
| import Effect (Effect)
| import Record.Unsafe (unsafeSet)
| import Unsafe.Coerce (unsafeCoerce)
| import Data.Traversable (for)
| import Control.Promise (fromAff) as Promise
| import Control.Promise (Promise)
| import Data.Set (Set)
| import Data.Set (toUnfoldable) as Set
|
| type Frontmatter = {}
|
| getMdxFiles :: EffectFn2 { remarkPlugins :: Array RemarkPlugin, rehypePlugins :: Array RehypePlugin } (Array String) (Promise (Array _))
| getMdxFiles = mkEffectFn2 \plugins globs -> Promise.fromAff do
| filePaths <- expandGlobsCwd globs <#> Set.toUnfoldable
| parsed <- for filePaths \filePath -> do
| source <- readTextFile UTF8 filePath
| { code, frontmatter } <- bundleMDX @Frontmatter
| { source
| , mdxOptions: mkEffectFn2 \opts (_ :: Frontmatter) -> do
| opts # addRehypePlugins plugins.rehypePlugins
| opts # addRemarkPlugins plugins.remarkPlugins
| pure opts
| , esbuildOptions: mkEffectFn2
| \(opts :: EsbuildOptions) (_ :: Frontmatter) -> do
| opts # setPlatform "node"
| opts # addLoader (Extension ".purs") "text"
| pure opts
| }
| let _ = spy "path" filePath
| pure { path: filePath, code, frontmatter }
| pure parsed

MDXBundler.js

| import {bundleMDX} from 'mdx-bundler'
|
| export const bundleMDXImpl = bundleMDX
|
| export const addRehypePluginsImpl = (plugins, config) => config.rehypePlugins = [...(config.rehypePlugins ?? []), ...plugins]
|
| export const addRemarkPluginsImpl = (plugins, config) => config.remarkPlugins = [...(config.remarkPlugins ?? []), ...plugins]
|
| export const setPlatformImpl = (value, config) => config.platform = value
|
| export const addLoaderImpl = (filetype, loader, config) => config.loader = {...config.loader, [filetype]: loader }

MDXBundler.purs

| module BuildTime.MDXBundler where
|
| import Prelude
| import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, runEffectFn1, runEffectFn2, runEffectFn3)
| import Data.Undefined.NoProblem (Opt, SNil)
| import Control.Promise (Promise, toAffE)
| import Data.Undefined.NoProblem.Open (class Coerce, class CoerceProp, coerce) as Open
| import Effect.Aff (Aff)
| import Effect (Effect)
| import Unsafe.Coerce (unsafeCoerce)
|
| foreign import data MdxOptions :: Type
| foreign import data Frontmatter :: Type
| foreign import data EsbuildOptions :: Type
| foreign import data RemarkPlugin :: Type
| foreign import data RehypePlugin :: Type
|
| type BundleMdxOptions frontmatter =
| { source :: String
| , mdxOptions :: Opt (EffectFn2 MdxOptions frontmatter MdxOptions)
| , esbuildOptions :: Opt (EffectFn2 EsbuildOptions frontmatter EsbuildOptions)
| }
|
| foreign import bundleMDXImpl :: forall frontmatter. EffectFn1
| (BundleMdxOptions frontmatter)
| (Promise { code :: String, frontmatter :: frontmatter })
|
| bundleMDX :: forall @frontmatter given.
| Open.Coerce given (BundleMdxOptions frontmatter)
| => given -> Aff { code :: String , frontmatter :: frontmatter }
|
| bundleMDX opts = toAffE (runEffectFn1 bundleMDXImpl (Open.coerce opts))
|
| foreign import addRehypePluginsImpl :: EffectFn2 (Array RehypePlugin) MdxOptions Unit
|
| foreign import addRemarkPluginsImpl :: EffectFn2 (Array RemarkPlugin) MdxOptions Unit
|
| addRehypePlugins :: (Array RehypePlugin) -> MdxOptions -> Effect Unit
| addRehypePlugins = runEffectFn2 addRehypePluginsImpl
|
| addRemarkPlugins :: (Array RemarkPlugin) -> MdxOptions -> Effect Unit
| addRemarkPlugins = runEffectFn2 addRemarkPluginsImpl
|
| foreign import setPlatformImpl :: EffectFn2 String EsbuildOptions Unit
|
| setPlatform :: String -> EsbuildOptions -> Effect Unit
| setPlatform = runEffectFn2 setPlatformImpl
|
| newtype Extension = Extension String
|
| foreign import addLoaderImpl :: EffectFn3 Extension String EsbuildOptions Unit
|
| addLoader :: Extension -> String -> EsbuildOptions -> Effect Unit
| addLoader = runEffectFn3 addLoaderImpl

page.tsx

| import {allPosts} from 'contentlayer/generated'
| import {default as Page} from "@/output/Alethic.Page.View"
| import {notFound} from "next/navigation";
| import {getMdxFiles} from "@/output/BuildTime.GetMDXFiles"
| import remarkGfm from "remark-gfm";
| import rehypeSlug from "rehype-slug";
| import rehypeAutolinkHeadings from "rehype-autolink-headings";
| import {h, s} from "hastscript";
| import rehypePrettyCode from "rehype-pretty-code";
| import {getHighlighter} from "@/contentlayer/shiki";
|
| const mdx = {
| remarkPlugins: [[remarkGfm]],
|
| rehypePlugins: [
| [rehypeSlug],
| [
| rehypeAutolinkHeadings,
| {
| behavior: "prepend",
| properties: {
| properties: {
| className: 'autolink',
| ariaHidden: true,
| tabIndex: -1,
| },
| },
| content: [
| h('span.autolink-info', {className: "sr-only"}, ' permalink'),
| s(
| 'svg.autolink-svg',
| {
| xmlns: 'http://www.w3.org/2000/svg',
| width: 20,
| height: 20,
| fill: 'currentColor',
| viewBox: '0 0 24 24',
| className: "opacity-0 group-hover:opacity-100"
| },
| s('path', {
| d: 'M9.199 13.599a5.99 5.99 0 0 0 3.949 2.345 5.987 5.987 0 0 0 5.105-1.702l2.995-2.994a5.992 5.992 0 0 0 1.695-4.285 5.976 5.976 0 0 0-1.831-4.211 5.99 5.99 0 0 0-6.431-1.242 6.003 6.003 0 0 0-1.905 1.24l-1.731 1.721a.999.999 0 1 0 1.41 1.418l1.709-1.699a3.985 3.985 0 0 1 2.761-1.123 3.975 3.975 0 0 1 2.799 1.122 3.997 3.997 0 0 1 .111 5.644l-3.005 3.006a3.982 3.982 0 0 1-3.395 1.126 3.987 3.987 0 0 1-2.632-1.563A1 1 0 0 0 9.201 13.6zm5.602-3.198a5.99 5.99 0 0 0-3.949-2.345 5.987 5.987 0 0 0-5.105 1.702l-2.995 2.994a5.992 5.992 0 0 0-1.695 4.285 5.976 5.976 0 0 0 1.831 4.211 5.99 5.99 0 0 0 6.431 1.242 6.003 6.003 0 0 0 1.905-1.24l1.723-1.723a.999.999 0 1 0-1.414-1.414L9.836 19.81a3.985 3.985 0 0 1-2.761 1.123 3.975 3.975 0 0 1-2.799-1.122 3.997 3.997 0 0 1-.111-5.644l3.005-3.006a3.982 3.982 0 0 1 3.395-1.126 3.987 3.987 0 0 1 2.632 1.563 1 1 0 0 0 1.602-1.198z',
| })
| ),
| ],
| },
| ],
| [rehypePrettyCode, {
| keepBackground: true,
| getHighlighter,
| onVisitLine: node => {
|
| if (node.children && node.children[0] && node.children[0].children[0] && node.children[0].children[0].value == 'import') {
| if (node.properties)
| if (node.properties.className) {
| node.properties.className.push(" import-line")
| } else {
| node.properties.className = ["import-line"]
| }
| }
|
| },
| onVisitHighlightedWord: (element) => {
| // Each word element has no className by default.
| element.properties.className = ['highlighted-word'];
| element.style = "color:#000"
| },
|
| }]
| ]
| }
|
|
| export const generateStaticParams = async () => {
| const mdxFiles = await getMdxFiles(mdx, ['./content/**/*.mdx'])
| console.log(mdxFiles)
| return [] // allPosts.filter(post => post.docType == 'page').map((post) => ({slug: post._raw.flattenedPath}))
| }