1
0
Fork 0
freesewing/sites/shared/prebuild/markdown.mjs

201 lines
5.2 KiB
JavaScript
Raw Normal View History

import fs from 'node:fs'
import path from 'node:path'
import { exec } from 'node:child_process'
/*
* Shared header to include in written .mjs files
*/
export const header = `/*
* This file was auto-generated by the prebuild script
* Any changes you make to it will be lost on the next (pre)build.
*/
`
/*
* Strips quptes from the start/end of a string
*/
const stripQuotes = (str) => {
str = str.trim()
if (str.slice(0, 1) === '"') str = str.slice(1)
if (str.slice(-1) === '"') str = str.slice(0, -1)
return str.trim()
}
/*
* This is the fast and low-tech way to some frontmatter from all files in a folder
*/
const loadFolderFrontmatter = async (key, site, folder, transform = false) => {
const prefix = site === 'org' ? 'docs/' : ''
/*
* Figure out what directory to spawn the child process in
*/
const cwd = await path.resolve(process.cwd(), '..', '..', 'markdown', site, folder)
let list = false
const grep = exec('grep ^' + key + ': -RIsm 1 ' + cwd, { cwd }, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`)
return
}
return stdout
})
/*
* Stdout is buffered, so we need to gather all of it
*/
let stdout = ''
for await (const data of grep.stdout) stdout += data
/*
* Turn all matches into an array
*/
const matches = stdout.split('\n')
/*
* Turns matches into structured data
*/
const pages = {}
for (let match of matches) {
/*
* Trim some of the irrelevant path info prior to splitting on '.md:{key}:'
*/
const chunks = match
.split(`markdown/${site}/${site === 'org' ? 'docs/' : ''}`)
.pop()
.split(`.md:${key}:`)
if (chunks.length === 2 && chunks[0].length > 1) {
/*
* Figure out the language and make sure we have an key for that language
*/
const lang = chunks[0].slice(-2)
if (!pages[lang]) pages[lang] = {}
/*
* Add page to our object with slug as key and title as value
*/
let slug = prefix + chunks[0].slice(0, -3)
if (slug === prefix) slug = slug.slice(0, -1)
pages[lang][slug] = transform
? transform(stripQuotes(chunks[1]), slug, lang)
: stripQuotes(chunks[1])
}
}
return pages
}
/*
* Merges in order key on those slugs that have it set
*/
const mergeOrder = (titles, order) => {
const pages = {}
for (const lang in titles) {
pages[lang] = {}
for (const [slug, t] of Object.entries(titles[lang])) {
pages[lang][slug] = { t }
if (order[slug]) pages[lang][slug] = order[slug]
}
}
return pages
}
/*
* Fixes the date format to be yyyymmdd
*/
const formatDate = (date, slug, lang) => {
date = date.split('-')
if (date.length === 1) date = date[0].split('.')
if (date.length === 1) console.log(`Could not format date ${date} from ${slug} (${lang})`)
else {
if (date[0].length === 4) return date.join('')
else return date.reverse().join('')
}
}
/*
* Loads all docs files, titles and order
*/
const loadDocs = async (site) => {
const folder = site === 'org' ? 'docs' : '.'
const titles = await loadFolderFrontmatter('title', site, folder)
const order = await loadFolderFrontmatter('order', site, folder)
return mergeOrder(titles, order)
}
/*
* Loads all blog posts, titles and order
*/
const loadBlog = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'blog')
const order = await loadFolderFrontmatter('date', 'org', 'blog', formatDate)
return mergeOrder(titles, order)
}
/*
* Loads all showcase posts, titles and order
*/
const loadShowcase = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'blog')
const order = await loadFolderFrontmatter('date', 'org', 'blog')
return mergeOrder(titles, order)
}
/*
* Loads all newsletter posts, titles and order
*/
const loadNewsletter = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'newsletter')
const order = await loadFolderFrontmatter('edition', 'org', 'newsletter')
return mergeOrder(titles, order)
}
/*
* Write out prebuild files
*/
const writeFiles = async (type, site, pages) => {
let allPaths = ``
for (const lang in pages) {
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `${type}.${lang}.mjs`),
`${header}export const pages = ${JSON.stringify(pages[lang])}`
)
allPaths += `import { pages as ${lang} } from './${type}.${lang}.mjs'` + '\n'
}
// Write umbrella file
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `${type}.mjs`),
`${allPaths}${header}
export const pages = { ${Object.keys(pages).join(',')} }`
)
}
/*
* Main method that does what needs doing for the docs
*/
export const prebuildDocs = async (store) => {
store.docs = await loadDocs(store.site)
await writeFiles('docs', store.site, store.docs)
}
/*
* Main method that does what needs doing for the blog/showcase/newsletter posts
*/
export const prebuildPosts = async (store) => {
store.posts = {
blog: await loadBlog(),
showcase: await loadShowcase(),
newsletter: await loadNewsletter(),
}
await writeFiles('blog', 'org', store.posts.blog)
await writeFiles('showcase', 'org', store.posts.showcase)
await writeFiles('newsletter', 'org', store.posts.newsletter)
}