import remark from "remark";
import html from "remark-html";
import fs from "fs";
import { join, parse } from "path";
import matter from "gray-matter";
import toml from "@iarna/toml";
// export async function markdownToHtml(markdown) {
// const result = await remark().use(html).process(markdown)
// return result.toString()
// }
const directories = {
blog: join(process.cwd(), "content/blog"),
events: join(process.cwd(), "content/events"),
docs: join(process.cwd(), "content/docs"),
};
export function getPostSlugs(key) {
return fs.readdirSync(directories[key]);
}
// NB Gavin: This could be much simpler if we converted everything to a more common standard, like YAML
const options = {
engines: {
toml: toml.parse.bind(toml),
},
language: "toml",
delimiters: "+++",
};
export function getPostBySlug(slug, fields = [], key) {
const realSlug = slug.replace(/\.md$/, "");
const fullPath = join(directories[key], `${realSlug}.md`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(fileContents, options);
const items = {};
// Ensure only the minimal needed data is exposed
fields.forEach((field) => {
if (field === "slug") {
items[field] = realSlug;
}
if (field === "content") {
items[field] = content;
}
if (data[field]) {
items[field] = data[field];
}
});
// NB Gavin: This could be removed if the TOML engine returned a JSON date (string) instead of a JS Date object. The YAML engine did this parsing by default.
items.date = JSON.stringify(items.date).replace(/\"/g, "");
return items;
}
export function getAllPosts(fields = [], key) {
const slugs = getPostSlugs(key);
const posts = slugs
.map((slug) => getPostBySlug(slug, fields, key))
// sort posts by date in descending order
.sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
return posts;
}
export function getNextPost(slug, fields = [], key) {
let resultPost = null;
getAllPosts(fields, key).forEach((post, index, array) => {
if (post.slug === slug) {
resultPost = array[index - 1];
}
});
return resultPost;
}
export function getPreviousPost(slug, fields = [], key) {
let resultPost = null;
getAllPosts(fields, key).forEach((post, index, array) => {
if (post.slug === slug) {
resultPost = array[index + 1];
}
});
return resultPost;
}
export function formatDate(d) {
// const wk = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
const mn = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
return mn[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
}
// export function capitalize(str) {
// return str.charAt(0).toUpperCase() + str.slice(1);
// }
// export function generateCrumbs(path) {
// const crumbs = path.split('/')
// return crumbs
// .filter(v => v !== '')
// .map((crumb, index) => ({
// name: capitalize(crumb),
// key: crumb,
// path: crumbs.slice(0, index+2).join('/'),
// }))
// }
export const getAllFiles = function (dirPath, arrayOfFiles) {
let files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
} else {
arrayOfFiles.push(join(__dirname, dirPath, "/", file));
}
});
return arrayOfFiles;
};
function get(obj, path, defaultValue) {
for (const index of path) {
if (obj[index] === undefined) {
return defaultValue;
}
obj = obj[index];
}
return obj;
}
function set(obj, path, value) {
let piece = obj;
for (let i = 0; i < path.length - 1; i++) {
if (piece[path[i]] === undefined) {
piece[path[i]] = typeof path[i + 1] === "number" ? [] : {};
}
piece = piece[path[i]];
}
piece[path[path.length - 1]] = value;
return obj;
}
/**
* buildPageTree is written to provide a recursive index of posts
* for nested directories for creating sidebars
* @param {string} path Absolute path to the subdirectory.
* @returns {Object.>} Title of the directory, child pages, and child directories (recursive structure).
*
*/
// type PageTree =
// { title: string,
// pages: [{
// title: string,
// base: string,
// slug: string,
// weight: number,
// }],
// children: { [string]: PageTree },
// }
export function buildPageTree(path) {
const metadata = matter(fs.readFileSync(join(path, "_index.md")), options);
const children = fs.readdirSync(path, { withFileTypes: true });
const pages = children.filter((f) => f.isFile() && f.name !== "_index.md");
const subdirs = children.filter((f) => f.isDirectory());
return {
title: metadata.data.title,
pages: pages.map((page) => {
const { data: pageData } = matter(
fs.readFileSync(join(path, page.name)),
options
);
return {
title: pageData.title,
base: page.name,
slug: page.name.replace(/.md$/, ""),
weight: pageData?.weight ?? 0,
};
}),
children: Object.fromEntries(
subdirs.map((subdir) => [
subdir.name,
buildPageTree(join(path, subdir.name)),
])
),
};
}
export const getPage = (path) => {
try {
let fileContents = fs.readFileSync(`${path}.md`, "utf8");
if (fileContents) {
const { data, content } = matter(fileContents, options);
return { data, content };
}
} catch {
try {
let fileContents = fs.readFileSync(`${path}/_index.md`, "utf8");
if (fileContents) {
const { data, content } = matter(fileContents, options);
return { data, content };
}
} catch {
console.error("no md file for slug");
}
}
};