Skip to content

Instantly share code, notes, and snippets.

@brianwisti
Last active September 4, 2021 15:59
Show Gist options
  • Save brianwisti/9334dfb194a5ffd8e4f9d6abbea2df23 to your computer and use it in GitHub Desktop.
Save brianwisti/9334dfb194a5ffd8e4f9d6abbea2df23 to your computer and use it in GitHub Desktop.
Use markdown-it to transform markdown files for consumption by other static site generators (Hugo specifically)
// This file processes a mess of FILE.md.txt files under Hugo `content/`,
// creating `index.html` files with HTML fragments and frontmatter.
// As far as Hugo's concerned it's just some more content, and I can prep
// for Astro or another Node-based SSG without disrupting the live site
// too much.
//
// Why? The Hugo version of my site is now SIX years old, and is a
// disorganized mishmash of formats and shortcodes. Porting it to
// anything else is what they call "non-trivial."
//
// Also this way I know what plugins to add in any SSG that uses markdown-it.
'use strict';
const fs = require('fs');
const path = require('path');
const MarkdownIt = require('markdown-it');
const glob = require("glob");
const matter = require('gray-matter');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const admonitionPattern = /^(?<category>note|admonition)\s?(?<title>.*)$/;
const md = MarkdownIt({ html: true, })
.use(require('markdown-it-abbr'))
.use(require('markdown-it-container'), 'note', {
validate: function(params) {
return params.trim().match(admonitionPattern);
},
render: function (tokens, idx) {
const m = tokens[idx].info.trim().match(admonitionPattern);
if (tokens[idx].nesting === 1) {
// opening tag
let { category, title } = m.groups;
if (! category ) {
return console.error("Incomplete admonition");
}
if (!title) {
title = category;
}
title = md.render(title);
return `<aside class="${category}">\n<header>${title}</header>\n`;
} else {
// closing tag
return '</aside>\n';
}
}
}
)
.use(require('markdown-it-deflist'))
.use(require('markdown-it-figure'))
.use(require('markdown-it-footnote'))
.use(require('markdown-it-kbd'))
.use(require('markdown-it-prism'), {
defaultLanguage: 'text',
});
const getTargetPath = (sourcePath) =>
path.join( path.dirname(sourcePath), 'index.html');
const transformFile = (sourcePath, targetPath) => {
fs.readFile(sourcePath, 'utf8', function (err, data) {
if (err) {
return console.error(err);
}
let post = matter(data);
post.content = md.render(post.content);
const transformed = post.stringify();
console.log(`${sourcePath} -> ${targetPath}`);
fs.writeFile(targetPath, transformed, (err) => {
if (err) {
return console.error(err);
}
console.log("Saved!");
})
});
};
const transformAllFiles = (contentDir) => {
console.log(`Transforming all files in ${contentDir}`);
const sourceGlob = `${contentDir}/**/*.md.txt`;
glob(sourceGlob, (err, sourcePaths) => {
if (err) {
return console.error(err);
}
for (let sourcePath of sourcePaths) {
const targetPath = getTargetPath(sourcePath);
fs.stat(sourcePath, (err, sourceStat) => {
if (err) {
return console.error(err);
}
fs.stat(targetPath, (err, targetStat) => {
if (err) {
return console.error(err);
}
if (sourceStat.mtime > targetStat.mtime) {
transformFile(sourcePath, targetPath);
}
});
});
}
});
};
yargs(hideBin(process.argv))
.command(
'file [md]', 'transform a markdown file',
(yargs) => {
return yargs
.positional('md', {
describe: 'markdown file to transform',
})
},
(argv) => {
const sourcePath = argv.md;
console.log(`Transforming ${sourcePath}`);
const targetPath = getTargetPath(sourcePath);
transformFile(sourcePath, targetPath);
}
)
.command(
'all [dir]', 'transform all markdown files',
(yargs) => {
return yargs
.positional('dir', {
describe: 'directory to work in',
default: 'content',
});
},
(argv) => {
transformAllFiles(argv.dir);
}
)
.argv;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment