Skip to content

Instantly share code, notes, and snippets.

@f-space
Last active February 24, 2021 11:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save f-space/57baa35d8bd83602e9e8bc19a9c8a091 to your computer and use it in GitHub Desktop.
Save f-space/57baa35d8bd83602e9e8bc19a9c8a091 to your computer and use it in GitHub Desktop.
Deno script to convert Markdown to RPG Maker forum format
// Command:
// $ deno run --allow-read --allow-write md2rmbb.ts source.md
// License:
// CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
import { Marked, Renderer } from "https://deno.land/x/markdown@v2.0.0/mod.ts";
import { parse } from "https://deno.land/std@0.88.0/flags/mod.ts";
import {
basename,
dirname,
resolve,
} from "https://deno.land/std@0.88.0/path/mod.ts";
const BB = (() => {
const tag = (name: string, attrs: readonly string[], body: string) => {
const option = attrs.length !== 0 ? `=${attrs.join(",")}` : "";
return `[${name}${option}]${body}[/${name}]`;
};
const bold = (body: string) => tag("B", [], body);
const italic = (body: string) => tag("I", [], body);
const underline = (body: string) => tag("U", [], body);
const strike = (body: string) => tag("S", [], body);
const color = (color: string, body: string) => tag("COLOR", [color], body);
const font = (name: string, body: string) => tag("FONT", [name], body);
const size = (size: string, body: string) => tag("SIZE", [size], body);
const url = (link: string, body: string) =>
tag("URL", link ? [link] : [], body);
const email = (address: string, body: string) =>
tag("EMAIL", address ? [address] : [], body);
const user = (id: string, body: string) => tag("USER", [id], body);
const img = (body: string) => tag("IMG", [], body);
const media = (site: string, body: string) => tag("MEDIA", [site], body);
const list = (ordered: boolean, body: string) =>
tag("LIST", ordered ? ["1"] : [], body);
const item = (body: string) => `[*] ${body}`;
const left = (body: string) => tag("LEFT", [], body);
const center = (body: string) => tag("CENTER", [], body);
const right = (body: string) => tag("RIGHT", [], body);
const quote = (person: string, body: string) =>
tag("QUOTE", person ? [person] : [], body);
const spoiler = (title: string, body: string) =>
tag("SPOILER", title ? [title] : [], body);
const ispoiler = (body: string) => tag("ISPOILER", [], body);
const code = (lang: string, body: string) =>
tag("CODE", lang ? [lang] : [], body);
const icode = (body: string) => tag("ICODE", [], body);
const indent = (body: string) => tag("INDENT", [], body);
const table = (body: string) => tag("TABLE", [], body);
const tr = (body: string) => tag("TR", [], body);
const th = (body: string) => tag("TH", [], body);
const td = (body: string) => tag("TD", [], body);
const heading = (level: number, body: string) =>
tag("HEADING", [String(level)], body);
const plain = (body: string) => tag("PLAIN", [], body);
const hr = () => tag("HR", [], "");
return {
bold,
italic,
underline,
strike,
color,
font,
size,
url,
email,
user,
img,
media,
list,
item,
left,
center,
right,
quote,
spoiler,
ispoiler,
code,
icode,
indent,
table,
tr,
th,
td,
heading,
plain,
hr,
};
})();
class ForumRenderer extends Renderer {
text(text: string) {
return text;
}
br() {
return "\n";
}
strong(text: string) {
return BB.bold(text);
}
em(text: string) {
return BB.italic(text);
}
del(text: string) {
return BB.strike(text);
}
link(href: string, _title: string, text: string) {
return BB.url(href, text);
}
image(href: string, _title: string, _text: string) {
return BB.img(href);
}
codespan(text: string) {
return BB.icode(text);
}
heading(text: string, level: number) {
if (level <= 3) {
return `${BB.heading(level, text)}\n`;
} else {
throw new Error(`h${level} not supported`);
}
}
paragraph(text: string) {
return /^\s*$/.test(text) ? "" : `${text.replace(/\n/g, "")}\n\n`;
}
list(body: string, ordered?: boolean) {
return `${BB.list(ordered ?? false, `\n${body}`)}\n\n`;
}
listitem(text: string) {
return `${BB.item(text)}\n`;
}
table(header: string, body: string) {
return `${BB.table(`\n${header}\n${body}`)}\n\n`;
}
tablerow(content: string) {
return `${BB.tr(content)}\n`;
}
tablecell(
content: string,
flags: { header?: boolean; align?: "center" | "left" | "right" },
) {
const align = (content: string, align?: "center" | "left" | "right") => {
switch (align) {
case undefined:
return content;
case "center":
return BB.center(content);
case "left":
return BB.left(content);
case "right":
return BB.right(content);
default:
throw new Error(`align '${align}' not supported`);
}
};
const body = align(content, flags.align);
return flags.header ? BB.th(body) : BB.td(body);
}
blockquote(quote: string) {
return `${BB.quote("", quote)}\n\n`;
}
code(code: string, lang?: string) {
return `${BB.code(lang ?? "", code)}\n\n`;
}
html(text: string) {
if (/^\s*<!--.*-->\s*$/.test(text)) {
return "";
} else {
throw new Error("html not supported");
}
}
hr() {
return BB.hr();
}
}
Marked.setOptions({
renderer: new ForumRenderer(),
escape: (s) => s,
});
const { _: args } = parse(Deno.args);
const src = String(args[0]);
const dst = resolve(dirname(src), basename(src, ".md") + ".txt");
const decoder = new TextDecoder();
const encoder = new TextEncoder();
const input = await Deno.readFile(src);
const markdown = decoder.decode(input);
const result = Marked.parse(markdown);
const output = encoder.encode(result.content);
await Deno.writeFile(dst, output);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment