MDX Frontmatter to Exports With Deno

May 17, 2020

🙌 Note: this is a short-form post. That means it's not very well researched and is mainly just my raw notes. I'm sharing it to help out anyone that may be investigating something similar, and also to try and maintain a writing habit!

Deno

Deno Launch Art

Aside from the fact I can't disassociate the name of this tool from that of an extraordinarily talented ex-colleague, Deno is all new to me.

It's a way to ship sandboxed TypeScript as a single executable. You can think of it as a cross-platform headless browser, with a package manager built-in.

Deno is a Node successor in the sense that it is designed for a new generation of JavaScript & TypeScript APIs, but it's probably nowhere near as stable yet. Node is ooooold. Deno is brand-spanking-new!

💡You can read mode about Deno in the author's own words at: https://deno.land/v1

MDX With Frontmatter to MDX With Exports

When I first created this blog, I used to provide custom metadata for each post like this:

---
title: Hello, deno!
path: /hello-deno
date: 2019-05-14
---

It's just YAML (which is fine), but it's possible (and slightly more fun) to define an export with JS like this:

export const meta = {
  "title": "Hello, deno!",
  "path": "/hello-deno",
  "date": "2019-05-14T00:00:00.000Z"
}

Christopher Biscardi (@chrisbiscardi) posted a handy little Node script to automate the migration a while back, and since I used it, I thought it would be a nice candidate for rewriting with Deno.

Here's the final thing (frontmatter-to-exports.ts):

import { parseAll } from "https://deno.land/std@0.51.0/encoding/yaml.ts";

let mdxDirectory = "./";

console.log(`Reading from directory: ${mdxDirectory}`);

for await (const file of Deno.readDir(mdxDirectory)) {
  if (!file.name.endsWith("mdx")) continue;
  console.log(`\n\nConverting '${file.name}':`);

  let content = await Deno.readTextFile("./example.mdx");

  let yaml = parseAll(content) as object[];
  const output = `export const meta = ${JSON.stringify(yaml[0], null, 2)}`;
  console.log(output);
}

Run it with: deno --allow-read frontmatter-to-exports.ts

There are a few differences from the original. Namely, it doesn't write the modified file back out. I did try to implement that, but without getting into some regex shenanigans, it wasn't particularly easy.

Problems

To be honest, this took me way longer than I expected to write, but it was a lot of fun.

TypeScript & Iterables

First I spent a while trying to write something vaguely functional-looking (thread), but gave up.

To be honest though, I think I'm kind of spoiled by Kotlin. The alternative I went with is just as readable anyway!

Ecosystem (Or... Lack Thereof)

Secondly I tried to figure out how to import some JS from the web but got this mysterious error:

Uncaught TypeError: Cannot resolve extension for "https://unpkg.com/browse/front-matter@3.2.1/index.js" with mediaType "Unknown".

Really crappy message, but I finally figured out to add -Ldebug to Deno, and got a nicer stacktrace:

⚠️️ Granted network access to
"https://unpkg.com/browse/front-matter@3.2.1/index.d.ts" unknown content type:
text/html; charset=utf-8

Turns out I was pointing at the wrong file, obviously I needed the raw one 🤦‍♂️.

👨‍💻 Importing code was the source of most of my pain. I didn't want to re-invent everything in the front-matter library, so I spent quite a while trying to reuse it. In the end though, since it had its own dependencies too, there was no simple way to do that.

As the Deno ecosystem develops (or the NPM shim gets finished), I'm sure this will get much easier! Until then though, it's probably very much 'roll-your-own' when it comes to stuff like this.


Handcrafted with TypeScript, React, Material-UI, and Gatsby ♥

© Morrison Cole 2020