mdToHtml? htmlToMd? How 'bout both!
Bryan Ollendyke
Posted on July 25, 2022
Micro frontend
Now with the DDG microservice built, let's connect two more practical micro frontends that are common in systems dealing with use based HTML input:
Our user writes markdown, they want HTML.
Our user writes HTML, they want markdown.
We could pull packages from npm into the browser to support this. It would make Alex Russell 😿, but We'D SaTiSfY ThE ReQuIrEmEnTs. Why would he 😢? Well, perhaps would too if we had awesome micro frontend architecture at our fingertips (and wallets) and yet still opted to send large JS packages down the wire.
To summarize the issue at hand we're about to:
load a large NPM package just to meet a feature requirement which has no reusability
turndown is 7.8KB
markdown-it is 30KB.
That's 37.8KB for functionality that the user might opt into at best, at worst you are already making decisions like this as if they are throw away so it's 37.8KB in an ocean of MBs.
We could lazy-load (and we do that with all sorta stuff to cheat on the KB metrics in our own stuff) however, the best JS payload is the payload not sent at all. So let's take the usefulness of solving a complex problem quickly via NPM packages, but apply it to our micro frontend approach.
Generic user input example
I've used both of these before to success in node projects so I decided to give it a try here. The code is highly readable for each using the approaches learned in the DDG example and so the process of taking things and making them micros flies by to this process:
have idea for functionality that seems like a single NPM package to solve
google for it, look for decent reviews / example
nameOfTheFunctionToPerform[.]js file creation
copy and update
MicroFrontendRegistry.add()
callpaste boilerplate, wire in exam from npm package
update package.json
yarn install && yarn start
and in a new terminalvercel dev
await MicroFrontendRegistry.call("@whatever/newFunction")
htmlToMd
import { stdPostBody, stdResponse, invalidRequest } from "../../../../utilities/requestHelpers.js";
import fetch from "node-fetch";
import * as df from 'turndown';
const TurndownService = df.default;
var turndownService = new TurndownService();
export default async function handler(req, res) {
const body = stdPostBody(req);
if (body === null) {
res = invalidRequest(res, 'missing body');
}
else if (!body.html) {
res = invalidRequest(res, 'missing `html` param');
}
else {
var html = body.html;
// md is actually a link reference so fetch it 1st
if (body.type === 'link' && html) {
html = await fetch(html.trim()).then((d) => d.ok ? d.text(): '');
}
stdResponse(res, await turndownService.turndown(html), {cache: 180 });
}
}
mdToHtml
import { stdPostBody, stdResponse, invalidRequest } from "../../../../utilities/requestHelpers.js";
import fetch from "node-fetch";
import * as df from 'markdown-it';
const MarkdownIt = df.default;
const mdClass = new MarkdownIt();
export default async function handler(req, res) {
const body = stdPostBody(req);
if (body === null) {
res = invalidRequest(res, 'missing body');
}
else if (!body.md) {
res = invalidRequest(res, 'missing `md` param');
}
else {
var md = body.md;
// md is actually a link reference so fetch it 1st
if (body.type === 'link' && md) {
md = await fetch(md.trim()).then((d) => d.ok ? d.text(): '');
}
stdResponse(res, await mdClass.render(md), {cache: 180 });
}
}
As you can see, both of these are very similar to the DDG example and even more similar to each other as one is a copy and modify of the other.
Both take either a content string of html or md and both have an additional flag for link
. The link
option allows for remote loading of HTML or MD via fetch()
. While fetch
is now part of Node v18+, our Vercel micros are running V16 so we still include the node-fetch package.
The actual implementation in HAX
So while the demo above is to illustrate capability, that's obviously not a production style implementation. This is though. Here's how we implemented both endpoints into HAX, our web component based editor for rich media and sustainable self expression.
In the next version of HAX, we now support downloading the current page as markdown. This makes HAX takes the page, converts to HTML, ships to the end point, and the endpoint sends back MD.
We also now support importing markdown into the body of content in HAX. As HAX is web component based, it's HTML centric. So to use MD from a source we need to convert it to the associated HTML. Simple enough now; just read the file, take it's content and ship it to the endpoint as MD, get back HTML, import it into the content of the page.
Videos showing it all together
Now the part where I make fun of my glasses and talk through how all the parts come together.
Part one
Establishing the requirements and showing it in context as well as in the stand alone demo
Part two
Code deep dive and showing how it's wired into HAX for the requirements in question.
Posted on July 25, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.