geminut/geminut.js

169 lines
4.5 KiB
JavaScript

const path = require("path");
const fs = require("fs"),
readline = require("readline");
let preformattedText = false;
//take input
const args = process.argv.slice(2);
const inputFile = args[0];
const outputFile = args[1];
let toMarkdown = false;
const header =
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="geminut" />
<link rel="stylesheet" href="gmi.css">
<title>` +
outputFile +
`</title>
</head>
<body>
`;
const footer = `
</body>
</html>
`;
const inputExtension = path.extname(inputFile);
const outputExtension = path.extname(outputFile);
if (outputExtension == ".md") {
toMarkdown = true;
}
if (!inputFile || !outputFile) {
console.log("USAGE: node geminut.js <input.gmi> {output.html | output.md}");
process.exit(1);
}
if (inputExtension !== ".gmi") {
console.log("Input must be a gemini .gmi file");
console.log("USAGE: node geminut.js <input.gmi> {output.html | output.md}");
process.exit(1);
}
if (!(outputExtension == ".html" || outputExtension == ".md")) {
console.log("Output must be a .html HTML or .md Markdown file");
console.log("USAGE: node geminut.js <input.gmi> {output.html | output.md}");
process.exit(1);
}
if (fs.existsSync(outputFile)) {
//if already exists we remove first
fs.unlinkSync(outputFile);
}
if (!toMarkdown) {
fs.appendFileSync(outputFile, header);
}
const rd = readline.createInterface({
input: fs.createReadStream(inputFile),
//output: process.stdout,
console: false
});
rd.on("line", function(line) {
// console.log(line);
let data;
if (toMarkdown) {
data = parseGemtextToMarkdown(line);
fs.appendFileSync(outputFile, data + "\n");
} else {
data = parseGemtextToHTML(line);
fs.appendFileSync(outputFile, data + "\n");
}
});
rd.on("close", function() {
if (!toMarkdown) {
fs.appendFileSync(outputFile, footer);
}
});
function parseGemtextToHTML(gmiText) {
let htmlText;
//CODE BLOCK SECTION PARSER / CONVERTER
if (gmiText.match(/^```*(.*$)/gim)) {
//We are triggering a pre/codeblock to start or end
//check for code block toggle
if (!preformattedText) {
htmlText = gmiText.replace(/^```*(.*$)/gim, "<pre>$1");
} else {
htmlText = gmiText.replace(/^```*(.*$)/gim, "</pre>$1");
}
preformattedText = !preformattedText; //toggle!
//END CODE BLOCK SECTION
} else {
//we are not on a line with pre/codeblock
if (!preformattedText) {
//outside of a codeblock
htmlText = gmiText
.replace(/^### (.*$)/gim, "<h3>$1</h3>")
.replace(/^## (.*$)/gim, "<h2>$1</h2>")
.replace(/^# (.*$)/gim, "<h1>$1</h1>")
.replace(/^\* (.*$)/gim, "<ul><li>$1</li></ul>") //hack, would be better to add to ul list
.replace(/^\> (.*$)/gim, "<blockquote>$1</blockquote>")
.replace(
/^=>\s+(\S+(.jpg|.jpeg|.png|.gif|.svg))\s*(.*)/gim,
"<figure>\n<img src='$1' alt='$3'>\n<figcaption>$3</figcaption>\n</figure>"
) //for images, must be before links replacement below or else will be captured and converted
.replace(/^=>\s+(\S+)\s*(.*)/gim, "<p><a href='$1'>$2</a></p>") //close, need to remove gmi
.replace(".gmi", ".html") //a hack. will break for remote *.gmi url's
.replace(/^([a-zA-Z0-9].*$)/gim, "<p>$1</p>"); //lines starting with text or number are paragraph
} else {
//we are inside a pre/codeblock
htmlText = gmiText;
}
}
return htmlText;
}
function parseGemtextToMarkdown(gmiText) {
let mdText;
//CODE BLOCK SECTION PARSER / CONVERTER
if (gmiText.match(/^```*(.*$)/gim)) {
//We are triggering a pre/codeblock to start or end
//check for code block toggle
if (!preformattedText) {
mdText = gmiText.replace(/^```*(.*$)/gim, "```$1");
} else {
mdText = gmiText.replace(/^```*(.*$)/gim, "```$1");
}
preformattedText = !preformattedText; //toggle!
//END CODE BLOCK SECTION
} else {
//we are not on a line with pre/codeblock
if (!preformattedText) {
//outside of a codeblock
mdText = gmiText
.replace(
/^=>\s+(\S+(.jpg|.jpeg|.png|.gif|.svg))\s*(.*)/gim,
"![$3]($1)"
) //for images, must be before links replacement below or else will be captured and converted
.replace(/^\* (.*$)/gim, "* $1\n") //hack, would be better to add to ul list
.replace(/^=>\s+(\S+)\s*(.*)/gim, "[$2]($1)\n") //close, need to remove gmi, add break
.replace(".gmi", ".md"); //a hack. will break for remote *.gmi url's
} else {
//we are inside a pre/codeblock
mdText = gmiText;
}
}
return mdText;
}