import { readFile } from 'fs/promises'
import { normalize, join, resolve, extname } from 'path'
import type { RouteHandlerOptions } from './types.ts'
const DOUBLE_CURLY_BRACKETS = /\{{(.*?)\}}/g // {{ foo }}
const DOUBLE_SQUARE_BRACKETS = /\[\[(.*?)\]]/g // [[ foo ]]
const DOUBLE_TAGS = /\<<(.*?)\>>/g // << foo >>
let extension = '.html'
const partialPaths = ['pages', 'snippets']
const layoutPaths = ['', 'layouts']
const getFile = (name: string) => async (path: string) => {
const fileName = extname(name) ? name : name + extension
return await readFile(join(resolve(path), normalize(fileName)), {
encoding: 'utf8',
})
}
export const renderLayout = async (fileContent: string, layout = 'index') => {
try {
const layoutfile = await Promise.any(layoutPaths.map(getFile(layout)))
const slot = ''
return layoutfile.replace(slot, fileContent)
} catch (error) {
return fileContent
}
}
const renderPartials = async (fileString: string) => {
const matches = [...fileString.matchAll(DOUBLE_SQUARE_BRACKETS)]
if (!matches.length) return fileString
let output = fileString
await Promise.all(
matches.map(async ([match, name]) => {
let content
try {
content = await Promise.any(partialPaths.map(getFile(name.trim())))
} catch (error) {
content = ''
console.log('404 - Missing partial: ' + match)
}
output = output.replace(match, content || '')
return
})
)
return await renderPartials(output)
}
export const renderVariables = (
fileString: string,
data: Record = {}
) => {
const matches = [...fileString.matchAll(DOUBLE_CURLY_BRACKETS)]
if (!matches.length) return fileString
return matches.reduce(
(acc, [match, variable]) =>
acc.replaceAll(match, data[variable.trim()]?.toString() || ''),
fileString
)
}
export const renderFileString = async (
fileString: string,
data?: Record
) => {
const layout = await renderLayout(fileString)
const partial = await renderPartials(layout)
const result = renderVariables(partial, data)
return result
}
export const renderFile = async (
name: string,
data?: Record
): Promise => {
try {
const filePromises = await Promise.allSettled(
partialPaths.map(getFile(name))
)
const file = filePromises.find(p => p.status === 'fulfilled')?.value || ''
return renderFileString(file, data)
} catch (error) {
console.log(error)
throw '500 - Found no partial to render: ' + name
}
}