157 lines
5.4 KiB
JavaScript
157 lines
5.4 KiB
JavaScript
// 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");
|
|
|
|
// 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;
|
|
|
|
// natives
|
|
const Process = require("process");
|
|
const Filesystem = require("fs");
|
|
const Path = require("path");
|
|
|
|
if(Process.argv.length < 4 && require.main === module) {
|
|
console.error("You need a source.md and target.htm path!");
|
|
Process.exit(1);
|
|
}
|
|
|
|
function markup(input, options) {
|
|
var usedSlugs = {};
|
|
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);
|
|
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 - */
|
|
/([(\[\-]|")\n\t*(<(?:code|a href=".+?")>.*<\/(?:code|a)>)/g,
|
|
"$1$2"
|
|
)
|
|
.replace( /* multitag suffix fix for special characters. */
|
|
/(<\/(?:a|code)>)\n\t*([.)\];:,?!+\-']|")/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>";
|
|
}
|
|
)
|
|
.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>"
|
|
);
|
|
}
|
|
// 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 */
|
|
}
|
|
|
|
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, " ")));
|
|
}
|