Transform Markdown into JavaScript with a Vite Plugin

  • javascript
  • typescript
  • vite
Jun 28, 2024
How to use the transform hook in a Vite plugin to import markdown files into modules, converting them to HTML strings.

Introduction

In this tutorial, I’ll show you how you can use the transform hook within a Vite plugin. We’ll create a way to import markdown files into JavaScript modules as a parsed HTML string.

If you haven’t created a plugin before, check out this tutorial: Create Your Own Vite Plugin.

Install dependencies

First, install marked as a dependency, we’ll use it to convert our markdown into HTML.

npm i marked

transform hook

We will use Rollup’s transform hook within our plugin, it allows us to import files that are not JavaScript by first compiling them to a module within the plugin. Lots of frameworks make use of this hook if they use custom file types. For example, vite-plugin-svelte uses this hook to convert .svelte files into JavaScript.

We’ll follow the example from the Vite documentation and create our markdown plugin. Let’s create a new plugin called markdown that converts .md files to JavaScript by adding it to our plugins array.

// vite.config.ts
import { parse } from "marked";
import { defineConfig } from "vite";

export default defineConfig({
	plugins: [
		{
			name: "markdown-html",
			async transform(code, id) {
				if (/\.(md)$/.test(id)) {
					// convert to html with `marked`
					const html = await parse(code);

					return {
						// return the .js file we would want to import
						code: `
							export const html = ${JSON.stringify(html)};
							export const md = ${JSON.stringify(code)};
						`,
						map: null,
					};
				}
			},
		},
	],
});

Types

Next, since I’m using TypeScript, I need to also tell the TypeScript language server that markdown files can now be imported. Since all files within src are included in our tsconfig, we can add a declaration file anywhere inside src to declare markdown files as modules.

// src/types/md.d.ts
declare module "*.md" {
	const html: string;
	const md: string;
	export { html, md };
}

This marks all .md files as modules that export an object containing html and md strings.

Test out the plugin

Next, create a .md file within your project to import.

<!-- src/md/post.md -->

# Heading 1

This is a markdown file.

- list item 1
- list item 2

Then test out the functionality within main.ts.

// src/main.ts
import { html } from "./md/post.md";

document.body.insertAdjacentHTML("afterbegin", html);

Run npm run dev to start the dev server and see the markdown rendered on the page!

Build time

It’s important to note, this import occurs at build time. Run npm run build to build your project and then inspect the dist/assets/index...js file. You’ll see the processed HTML inlined into this JavaScript file.

Conclusion

Utilize Rollup’s transform hook within a plugin to compile non-JS files into modules at build time.

Thanks for reading!


Edit