yiki/compile.js

157 lines
5.4 KiB
JavaScript
Raw Normal View History

2024-04-16 09:41:10 -04:00
// Copyright (c) 2024 yellows111
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// local
const Package = require("./package.json");
2024-04-16 09:41:10 -04:00
// npm
const Remarkable = require("remarkable").Remarkable; // remove .Remarkable if you're using Remarkable ^1.0.0!
const TOCGenerator = require("markdown-toc");
const RemarkableHeaderIDs = require("remarkable-header-ids");
const HTMLFormatter = require("@liquify/prettify").formatSync;
2024-04-16 09:41:10 -04:00
// natives
const Process = require("process");
const Filesystem = require("fs");
const Path = require("path");
if(Process.argv.length < 4 && require.main === module) {
2024-04-16 09:41:10 -04:00
console.error("You need a source.md and target.htm path!");
Process.exit(1);
}
function markup(input, options) {
var usedSlugs = {};
2024-04-16 09:41:10 -04:00
let tableOfContents = new Remarkable().use(TOCGenerator.plugin(options)).render(input);
let output = new Remarkable().use(
RemarkableHeaderIDs({
levels: [1,2,3,4,5,6],
anchorClassName: "header-anchor",
anchorText: "#",
headerId: function(slug) {
if(usedSlugs[slug] > 0) {
const out_ = `${slug}-${usedSlugs[slug]}`;
usedSlugs[slug] = usedSlugs[slug] + 1;
return out_;
} else {
if(!usedSlugs.hasOwnProperty(slug)) {
usedSlugs[slug] = 0;
}
usedSlugs[slug] = usedSlugs[slug] + 1;
return slug;
}
}
})
).render("# Table of contents:\n"+tableOfContents.content+"\n\n"+input);
2024-04-16 09:41:10 -04:00
return output;
}
function format(data, name) {
var wikiName = "A new yiki!";
var rootPrefix = "/";
if(process.env["WIKINAME"]) {
wikiName = process.env["WIKINAME"];
}
if(process.env["VPREFIX"]) {
rootPrefix = process.env["VPREFIX"];
}
// forceIndent screws with text nodes pretty bad but the alternative is to not allow the full document to be formatted
const formatterRules = {"indentChar": "\t", "indentSize": 1, "markup": {"forceAttribute": false, "forceIndent": true}};
function render() {
return HTMLFormatter(data, formatterRules)
.split("\n") /* split by every newline */
.join("\n\t\t\t") /* then add padding and convert back into string */
.replace( /* multitag text node fix (should really just encase all text with <span>, but whatever) */
/(\t*)(<(?:code|a href=".+?")>)\n\t*(.*?)\n\t*(<\/(?:code|a)>)/g,
"$1$2$3$4"
)
.replace( /* multitag prefix fix for (, [ and - */
/([(\[\-]|&quot;)\n\t*(<(?:code|a href=".+?")>.*<\/(?:code|a)>)/g,
"$1$2"
)
.replace( /* multitag suffix fix for special characters. */
/(<\/(?:a|code)>)\n\t*([.)\];:,?!+\-']|&quot;)/g,
"$1$2"
)
.replace( /* fix tag attached to word and not whitespace */
/(\w+)(<[a-zA-Z]\w*>)/g,
"$1 $2"
)
.replace( /* replace .md links to html ones */
/href="(.+?)\.md"/g,
"href=\"$1.html\""
)
.replace( /* evil codeblock fix */
/<pre><code>((.|\n)*?)<\/code><\/pre>/g,
function(match, inline) {
return "<pre><code>" + inline.replace(/\t/g, "") + "</code></pre>";
}
2024-10-11 12:56:31 -04:00
)
.replace( /* fix monospace'd links */
/<a href="(.+?)">\n\t+<code>(.*?)<\/code>\n\t+<\/a>/g,
"<a href=\"$1\"><code>$2</code></a>"
)
.replace( /* add one space to words that have a tag next to them */
/([a-zA-Z0-9]+?)<a/g,
"$1 <a"
)
.replace( /* fix randomly going to newline when subformatting links */
/([a-zA-Z0-9]+?)\n\t+<\/a>/g,
"$1</a>"
);
}
2024-04-16 09:41:10 -04:00
// probably should bring in a better templating engine but whatever
return ( /* eslint-disable indent */
`<!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=2.0">
<meta name="description" content="${name} on ${wikiName}">
<title>${name} - Documentation</title>
<link rel="stylesheet" type="text/css" href="${rootPrefix}yiki.css">
</head>
<body>
<nav>
<span>${wikiName}</span>
<span> | </span>
<a href="${rootPrefix}index.html">home</a>
</nav>
<hr>
<main>
${render()}
</main>
<hr>
<footer>
<span>page rendered by ${Package.name} ${Package.version} on ${new Date().toGMTString()}</span>
</footer>
</body>
</html>`
); /* eslint-enable indent */
2024-04-16 09:41:10 -04:00
}
module.exports = {"markup": markup, "format": format};
if(require.main === module) {
let content = markup(Filesystem.readFileSync(Process.argv[2]).toString());
Filesystem.writeFileSync(Process.argv[3], format(content, Path.basename(Process.argv[3], ".html").replace(/_/g, " ")));
}