mdToHtml? htmlToMd? How 'bout both!

btopro

Bryan Ollendyke

Posted on July 25, 2022

mdToHtml? htmlToMd? How 'bout both!

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:

  1. load a large NPM package just to meet a feature requirement which has no reusability

  2. turndown is 7.8KB

  3. 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() call

  • paste boilerplate, wire in exam from npm package

  • update package.json

  • yarn install && yarn start and in a new terminal vercel 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 });

}

}

Enter fullscreen mode Exit fullscreen mode

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 });

}

}

Enter fullscreen mode Exit fullscreen mode

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.

Download Markdown

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.

Import Markdown

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.

💖 💪 🙅 🚩
btopro
Bryan Ollendyke

Posted on July 25, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

mdToHtml? htmlToMd? How 'bout both!
microservices mdToHtml? htmlToMd? How 'bout both!

July 25, 2022