Skip to content

Instantly share code, notes, and snippets.

@cdoremus
Created September 19, 2023 21:49
Show Gist options
  • Save cdoremus/2a4956d815933b5dd43147582eaaffa8 to your computer and use it in GitHub Desktop.
Save cdoremus/2a4956d815933b5dd43147582eaaffa8 to your computer and use it in GitHub Desktop.
Using markdown with Deno
// from Fresh repo at: https://github.com/denoland/fresh/blob/4a8bfc03278068cffb017ed08d3eb891d7509c37/www/utils/markdown.ts#L13
export * as gfm from "https://deno.land/x/[email protected]/mod.ts";
import "https://esm.sh/[email protected]/components/prism-jsx.js?no-check";
import "https://esm.sh/[email protected]/components/prism-typescript.js?no-check";
import "https://esm.sh/[email protected]/components/prism-tsx.js?no-check";
import "https://esm.sh/[email protected]/components/prism-diff.js?no-check";
import "https://esm.sh/[email protected]/components/prism-json.js?no-check";
import "https://esm.sh/[email protected]/components/prism-bash.js?no-check";
import "https://esm.sh/[email protected]/components/prism-yaml.js?no-check";
export { extract as frontMatter } from "$std/front_matter/yaml.ts";
import Prism from "https://esm.sh/[email protected]";
import * as Marked from "https://esm.sh/[email protected]";
import { escape as escapeHtml } from "$std/html/entities.ts";
import { mangle } from "$marked-mangle";
Marked.marked.use(mangle());
class DefaultRenderer extends Marked.Renderer {
text(text: string): string {
// Smartypants typography enhancement
return text
.replaceAll("...", "…")
.replaceAll("--", "—")
.replaceAll("---", "–")
.replaceAll(/(\w)'(\w)/g, "$1’$2")
.replaceAll(/s'/g, "s’")
.replaceAll("'", "’")
.replaceAll(/["](.*?)["]/g, "“$1&#8221")
.replaceAll(/"(.*?)"/g, "“$1&#8221")
.replaceAll(/['](.*?)[']/g, "‘$1’");
}
heading(
text: string,
level: 1 | 2 | 3 | 4 | 5 | 6,
raw: string,
slugger: Marked.Slugger,
): string {
const slug = slugger.slug(raw);
return `<h${level} id="${slug}"><a class="anchor" aria-hidden="true" tabindex="-1" href="#${slug}">#</a>${text}</h${level}>`;
}
link(href: string, title: string | null, text: string) {
const titleAttr = title ? ` title="${title}"` : "";
if (href.startsWith("#")) {
return `<a href="${href}"${titleAttr}>${text}</a>`;
}
if (this.options.baseUrl) {
try {
href = new URL(href, this.options.baseUrl).href;
} catch (_) {
//
}
}
return `<a href="${href}"${titleAttr} rel="noopener noreferrer">${text}</a>`;
}
image(src: string, title: string | null, alt: string | null) {
return `<img src="${src}" alt="${alt ?? ""}" title="${title ?? ""}" />`;
}
code(code: string, info: string | undefined): string {
// format: tsx
// format: tsx my/file.ts
// format: tsx "This is my title"
let lang = "";
let title = "";
const match = info?.match(/^(\w+)\s*(.*)?$/);
if (match) {
lang = match[1].toLocaleLowerCase();
title = match[2] ?? "";
}
let out = `<div class="fenced-code">`;
if (title) {
out += `<div class="fenced-code-header">
<span class="fenced-code-title lang-${lang}">
${title ? escapeHtml(String(title)) : "&nbsp;"}
</span>
</div>`;
}
const grammar = lang && Object.hasOwnProperty.call(Prism.languages, lang)
? Prism.languages[lang]
: undefined;
if (grammar === undefined) {
out += `<pre><code class="notranslate">${escapeHtml(code)}</code></pre>`;
} else {
const html = Prism.highlight(code, grammar, lang);
out +=
`<pre class="highlight highlight-source-${lang} notranslate lang-${lang}">${html}</pre>`;
}
out += `</div>`;
return out;
}
}
export interface MarkdownOptions {
inline?: boolean;
}
export function renderMarkdown(
input: string,
opts: MarkdownOptions = {},
): string {
const markedOpts: Marked.MarkedOptions = {
gfm: true,
renderer: new DefaultRenderer(),
};
const html = opts.inline
? Marked.parseInline(input, markedOpts) as string
: Marked.parse(input, markedOpts) as string;
return html;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment