|
|
@ -1,17 +1,26 @@
|
|
|
|
import { promises as fs } from 'fs'
|
|
|
|
import { promises as fs } from 'fs'
|
|
|
|
import path from 'path'
|
|
|
|
import path from 'path'
|
|
|
|
|
|
|
|
import wabtModule from 'wabt'
|
|
|
|
|
|
|
|
|
|
|
|
import frontmatter from 'front-matter'
|
|
|
|
import frontmatter from 'front-matter'
|
|
|
|
|
|
|
|
|
|
|
|
export interface Post {
|
|
|
|
export interface PostMeta {
|
|
|
|
slug: string,
|
|
|
|
slug: string,
|
|
|
|
date: Date,
|
|
|
|
date: Date,
|
|
|
|
title: string,
|
|
|
|
title: string,
|
|
|
|
subtitle?: string,
|
|
|
|
subtitle?: string,
|
|
|
|
body: string,
|
|
|
|
|
|
|
|
unlisted: boolean,
|
|
|
|
unlisted: boolean,
|
|
|
|
|
|
|
|
filePath: string,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface Post extends Omit<PostMeta, "filePath"> {
|
|
|
|
|
|
|
|
body: string,
|
|
|
|
|
|
|
|
script?: string,
|
|
|
|
|
|
|
|
wasm?: Uint8Array,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Attributes = { [key: string]: any }
|
|
|
|
|
|
|
|
|
|
|
|
const POST_FILE_PATTERN = /^(?<date>[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]{1,2})?)_(?<slug>[^\.]+)\.md$/
|
|
|
|
const POST_FILE_PATTERN = /^(?<date>[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]{1,2})?)_(?<slug>[^\.]+)\.md$/
|
|
|
|
|
|
|
|
|
|
|
|
export async function getPostSlugs(): Promise<string[]> {
|
|
|
|
export async function getPostSlugs(): Promise<string[]> {
|
|
|
@ -26,7 +35,7 @@ export function getSlugFromFilePath(postPath: string): string | null {
|
|
|
|
return regexResult.groups.slug
|
|
|
|
return regexResult.groups.slug
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function loadSinglePage(slug: string): Promise<Post | null> {
|
|
|
|
export async function loadPageMetada(slug: string): Promise<PostMeta | null> {
|
|
|
|
const postsDir = path.join(process.cwd(), 'posts')
|
|
|
|
const postsDir = path.join(process.cwd(), 'posts')
|
|
|
|
const postPaths = await fs.readdir(postsDir)
|
|
|
|
const postPaths = await fs.readdir(postsDir)
|
|
|
|
const postMatch: RegExpExecArray | null | undefined = postPaths
|
|
|
|
const postMatch: RegExpExecArray | null | undefined = postPaths
|
|
|
@ -37,10 +46,9 @@ export async function loadSinglePage(slug: string): Promise<Post | null> {
|
|
|
|
if (!postMatch) return null;
|
|
|
|
if (!postMatch) return null;
|
|
|
|
|
|
|
|
|
|
|
|
const fileName = `${postMatch.groups?.date}_${postMatch.groups?.slug}.md`
|
|
|
|
const fileName = `${postMatch.groups?.date}_${postMatch.groups?.slug}.md`
|
|
|
|
const fileContents = (await fs.readFile(path.join(process.cwd(), 'posts', fileName))).toString('utf8')
|
|
|
|
const filePath = path.join(process.cwd(), 'posts', fileName)
|
|
|
|
const { attributes, body } = frontmatter(fileContents)
|
|
|
|
const fileContents = (await fs.readFile(filePath)).toString('utf8')
|
|
|
|
|
|
|
|
const { attributes } = frontmatter(fileContents)
|
|
|
|
type Attributes = { [key: string]: any }
|
|
|
|
|
|
|
|
const data: Attributes = attributes as Attributes
|
|
|
|
const data: Attributes = attributes as Attributes
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.title) return null;
|
|
|
|
if (!data.title) return null;
|
|
|
@ -49,11 +57,53 @@ export async function loadSinglePage(slug: string): Promise<Post | null> {
|
|
|
|
const date = new Date(Date.UTC(parseInt(year), parseInt(month) - 1, parseInt(day)))
|
|
|
|
const date = new Date(Date.UTC(parseInt(year), parseInt(month) - 1, parseInt(day)))
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
slug: postMatch.groups?.slug || '',
|
|
|
|
|
|
|
|
date,
|
|
|
|
date,
|
|
|
|
|
|
|
|
filePath,
|
|
|
|
|
|
|
|
slug: postMatch.groups?.slug || '',
|
|
|
|
title: data.title,
|
|
|
|
title: data.title,
|
|
|
|
subtitle: data.subtitle,
|
|
|
|
subtitle: data.subtitle,
|
|
|
|
unlisted: typeof data.unlisted === 'boolean' ? data.unlisted : false,
|
|
|
|
unlisted: typeof data.unlisted === 'boolean' ? data.unlisted : false,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function loadSinglePage(slug: string): Promise<Post | null> {
|
|
|
|
|
|
|
|
const postMeta = await loadPageMetada(slug)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!postMeta) return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fileContents = (await fs.readFile(postMeta.filePath)).toString('utf8')
|
|
|
|
|
|
|
|
const { attributes, body } = frontmatter(fileContents)
|
|
|
|
|
|
|
|
const data: Attributes = attributes as Attributes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (data.script) {
|
|
|
|
|
|
|
|
const scriptPath = path.join(process.cwd(), 'public/scripts', data.script)
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
await fs.readFile(scriptPath)
|
|
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
|
|
throw new Error(`Could not find script ${scriptPath} referenced in post ${slug}`)
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
data.script = `/scripts/${data.script}`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (data.wasm) {
|
|
|
|
|
|
|
|
const wabt = await wabtModule()
|
|
|
|
|
|
|
|
const wasmPath = path.join(process.cwd(), 'public/scripts', data.wasm)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fileName = data.wasm.replace(/^.*\//, '')
|
|
|
|
|
|
|
|
const wasmText = (await fs.readFile(wasmPath))
|
|
|
|
|
|
|
|
const wasmIntermed = wabt.parseWat(fileName, wasmText)
|
|
|
|
|
|
|
|
const { buffer: wasmBinary } = wasmIntermed.toBinary({ write_debug_names: true })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data.wasm = wasmBinary
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { filePath, ...otherMeta } = postMeta
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...otherMeta,
|
|
|
|
|
|
|
|
script: data.script,
|
|
|
|
|
|
|
|
wasm: data.wasm,
|
|
|
|
body
|
|
|
|
body
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|