ToC is now automaticcaly server-side rendered.

This commit is contained in:
BurnyLlama 2021-08-05 23:27:11 +02:00
parent dd376a22af
commit 0f3acaf7c2
18 changed files with 151 additions and 334 deletions

View File

@ -28,10 +28,10 @@ I can *promise* you that there is no malicious code or dependency in this projec
The project depends on the following packages (installed when you ran `npm i`):
* express (a web server framework)
* nunjucks (a templating engine)
* nunjucks-append (an extension that adds more functionality to nunjucks)
* nunjucks-markdown (markdown support in nunjucks)
* marked (markdown parser/compiler)
* highlight.js (syntax highlighting)
* jsdom (an extension that can use the DOM server-side - used to render the ToC)
* chokidar (reload templates if they change on disk)
* fs (used for filesystem operations)

View File

@ -1,12 +0,0 @@
const niceObject = {
type: "the greatest",
knownFor: [
"indenting",
"everything",
{
and: "wanting",
to: "keep",
it: "so"
}
]
}

View File

@ -1,9 +1,5 @@
{% macro header(size, text) %}
{% append "toc" %}
<a href="#{{ text | replace(" ", "-") }}" class="toc-{{ size }}">{{ text }}</a>
{% endappend %}
<a name="{{ text | replace(" ", "-") }}"></a>
<a name="{{ text | replace(" ", "-") }}" data-orig-text="{{ text }}" class="toc-anchor toc-anchor-{{ size }}"></a>
<{{ size }}>
{{ text }}
</{{ size }}>

View File

@ -1,106 +0,0 @@
{% extends "templates/article.njk" %}
{% import "components/toc.njk" as toc with context %}
{% block header %}
How to - Article template
{% endblock %}
{% block toc %}
{% output "toc" %}{% endoutput %}
{% endblock %}
{% block body %}
<div class="content">
{{ toc.header("h1", "Introduction", tocList) }}
<p>
I want to start off by saying that this article template (as of now) is really fricking shit.
The code base is somewhat okay in complexity, but I do not feel like it is done in a efficient/good
way and won't work without CSS enabled (although, most people don't disable CSS).
Please use with causition until I've implemented something better that would work without CSS.
</p>
{{ toc.header("h2", "Terminology", tocList) }}
<p>
Whenever I say "ToC" in this article I am refering to "Table of Contents". (I couldn't be bothered
writing it out all the times...)
</p>
{{ toc.header("h1", "Usage", tocList ) }}
<p>
To use the template you would simply extend <code>templates/article.njk</code> and
import <code>components/toc.njk</code>:
<code class="code-block">
{{ '
{% extends "templates/article.njk" %}
{% import "components/toc.njk" as toc with context %}
{% block header %}
Your header
{% endblock %}
{% block body %}
<div class="content">
<p>
Your content goes here
</p>
</div>
{% endblock %}' | escape | replace("\n", "", 1) | replace("\n", "<br>") | replace(" ", "&nbsp;") }}
</code>
You would use the <code>header</code> block to define a header, and then you place your
content inside a <code>div</code> with the class <code>content</code> in your <code>body</code>-block.
</p>
{{ toc.header("h2", "Adding headers to the ToC", tocList) }}
<p>
Adding headers is kind of easy, <code>{% raw %}{% import "components/toc.njk" as toc with context %}{% endraw %}</code>
imports a macro that will create a header and automatically add it to the table of contents. You use it as such:
<code class="code-block">
{{ '{{ toc.header(size, text, tocList) }}' }}
</code>
<code>size</code> is a string and is a header level (aka <code>h1</code>, <code>h2</code>, <code>h3</code>, etc.),
<code>text</code> is a string that holds the text you would want to display, and finally <code>tocList</code> is a
variable inherited from <code>template/article.njk</code> that keeps a hold of which headers should go in the ToC. <br>
If you would want to display an <code>{{ '<h1>' }}</code> with the text "Introduction" you would do:
<code class="code-block">
{{ '{{ toc.header("h1", "Introduction", tocList) }}' }}
</code>
</p>
{{ toc.header("h2", "Generate the ToC", tocList) }}
<p>
Sadly it isn't enough to just add the heading to the ToC. You will have to generate the ToC as well, but
this isn't hard at all to do. You simply add <code>{{ '{{ toc.generate(tocList) }}' }}</code> to the end
of your <code>body</code>-block.
</p>
{{ toc.header("h1", "Example code", tocList) }}
<code class="code-block">
{{ '
{% extends "templates/article.njk" %}
{% import "components/toc.njk" as toc with context %}
{% block header %}
Your header
{% endblock %}
{% block body %}
<div class="content">
{{ toc.header("h1", "Introduction", tocList) }}
<p>
Your introduction goes here...
</p>
</div>
{{ toc.generate(tocList) }}
{% endblock %}' | escape | replace("\n", "", 1) | replace("\n", "<br>") | replace(" ", "&nbsp;") }}
</code>
</div>
{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends "templates/article.njk" %}
{% import "components/toc.njk" as toc with context %}
{% import "components/code.njk" as code with context %}
{% block header %}
Markdown
{% endblock %}
{% block toc %}
{% output "toc" %}{% endoutput %}
{% endblock %}
{% block body %}
{% markdown %}
# Test of markdown
{{ code.import("code/test.js", "javascript") }}
* A list item
* A list item
* A list item
* A list item
*italic* **bold**
{{ toc.header("h1", "test header") }}
{% endmarkdown %}
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "templates/article.njk" %}
{% import "components/toc.njk" as toc with context %}
{% block head %}
<title>qwik cms - docs - main</title>
{% endblock %}
{% block header %}
qwik cms - docs
{% endblock %}
{% block main %}
{{ toc.header("h1", "Basic idea") }}
<p>
Our CMS can be used in multiple (different) ways thanks to the underlying technologies.
The basic idea is that every page is a file. Although how you treat that file is really
up to you. It can be plain HTML, nunjucks/njk (a templating language for HTML) or even
markdown (although not "plain markdown").
</p>
{{ toc.header("h2", "Views on bloat") }}
<p>
I do not want to bloat this CMS, but I must admit that it is bloated.
{{ hello }}
</p>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "templates/article_md.njk" %}
{% block head %}
<title>qwik cms - docs - main</title>
{% endblock %}
{% block header %}
qwik cms - docs
{% endblock %}
{% block main %}
# Test
Heyyy you there!
## Undertest
{% endblock %}

View File

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
<p>
Welcome to {{ serverName }}! <br>
<a href="/test1">Test 1</a> <br>
<a href="/test2">Test 2</a> <br>
</p>
</body>
</html>

View File

@ -1,3 +0,0 @@
<p>
Test 1
</p>

View File

@ -1,3 +0,0 @@
<p>
Test 2
</p>

View File

@ -1,133 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
{% include "templates/defaultTags.njk" %}
<title>Document</title>
</head>
<body>
<header>
Actual Header
</header>
<h1>Header 1</h1>
<h2>Header 2</h2>
<h3>Header 3</h3>
<h4>Header 4</h4>
<h5>Header 5</h5>
<h6>Header 6</h6>
<p>
This is a paragraph. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptate non ut commodi quisquam in, a dicta rem expedita tenetur quis ratione aut nesciunt corporis soluta similique eius accusantium, optio numquam? Dolorem, voluptates!
</p>
<p>
<i>Italic text</i> <br>
<b>Bold text</b> <br>
<a href="./">This is an example link</a>
</p>
<blockquote>
This is a blockquote. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem voluptates soluta dolorem esse aperiam natus provident nemo, dicta dolores quae velit officia minus expedita cum modi eveniet minima suscipit mollitia aspernatur ad.
</blockquote>
<ul>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<ul>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<ul>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
</ul>
</ul>
</ul>
<ol>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<li>This is a list item!</li>
<ol>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<ol>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
<li>This is a nested list.</li>
</ol>
</ol>
</ol>
<table>
<tr>
<th>Animal</th>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>Bunny</td>
<td>Jumpy</td>
<td>2 years</td>
</tr>
<tr>
<td>Cat</td>
<td>Biter</td>
<td>4 months</td>
</tr>
<tr>
<td>Bunny</td>
<td>Jumpy</td>
<td>2 years</td>
</tr>
<tr>
<td>Cat</td>
<td>Biter</td>
<td>4 months</td>
</tr>
<tr>
<td>Bunny</td>
<td>Jumpy</td>
<td>2 years</td>
</tr>
<tr>
<td>Cat</td>
<td>Biter</td>
<td>4 months</td>
</tr>
<tr>
<td>Bunny</td>
<td>Jumpy</td>
<td>2 years</td>
</tr>
<tr>
<td>Cat</td>
<td>Biter</td>
<td>4 months</td>
</tr>
</table>
<code class="code-block">
{{ '
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
' | escape | replace("\n", "", 1) | replace("\n", "<br>") | replace(" ", "&nbsp;") }}
</code>
</body>
</html>

View File

@ -17,7 +17,10 @@
<div class="toc-container">
<div class="toc">
<p class="toc-title">Table of Contents</p>
{% block toc %}{% endblock %}
{% for tocLink in tocLinks %}
{{ tocLink | safe }}
{% endfor %}
</div>
</div>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
{% include "./defaultTags.njk" %}
<link rel="stylesheet" href="/assets/css/article.css">
<link rel="stylesheet" href="/assets/css/syntax.css">
{% block head %}
<title>{{ serverName }}</title>
{% endblock %}
</head>
<body>
<article>
<header>
{% block header %}{% endblock %}
</header>
<div class="toc-container">
<div class="toc">
<p class="toc-title">Table of Contents</p>
{% for tocLink in tocLinks %}
{{ tocLink | safe }}
{% endfor %}
</div>
</div>
{% markdown %}
{% block main %}{% endblock %}
{% endmarkdown %}
</article>
</body>
</html>

View File

@ -1,14 +1,17 @@
import express from 'express'
import njk from 'nunjucks'
import njkAppend from 'nunjucks-append'
import njkMarkdown from 'nunjucks-markdown'
import marked from 'marked'
import fs from 'fs'
import { requestHandler } from './libs/requestHandler.js'
import express from 'express'
import njk from 'nunjucks'
import njkMarkdown from 'nunjucks-markdown'
import marked from 'marked'
import hljs from 'highlight.js'
import { requestHandler } from './libs/requestHandler.js'
import { mdRenderer } from './libs/mdRenderer.js'
// Load in config
const ConfigFile = fs.readFileSync('./config.json')
const Config = JSON.parse(ConfigFile)
@ -16,12 +19,15 @@ const Config = JSON.parse(ConfigFile)
const Server = express()
// Some config
Server.use('/assets', express.static(Config.assetsDir))
const njkEnv = njk.configure(
Config.contentDir,
{
autoescape: false,
autoescape: true,
watch: true,
trimBlocks: true,
lstripBlocks: true,
@ -35,21 +41,21 @@ marked.setOptions({
return hljs.highlight(language, code)
.value.replace(/\n/g, "<br>").replace(/•/g, "&nbsp;")
},
gfm: true,
smartypants: true,
smartLists: true,
silent: true
gfm: true
})
njkAppend.initialise(njkEnv)
marked.use({ renderer: mdRenderer })
njkMarkdown.register(njkEnv, marked)
Server.get(
'*',
(req, res) => requestHandler(req, res, Config)
)
// Send all requests to the requestHandler.
Server.get('*', (req, res) => requestHandler(req, res, Config))
// Start the server
Server.listen(
Config.serverPort,
() => {

10
libs/mdRenderer.js Normal file
View File

@ -0,0 +1,10 @@
export const mdRenderer = {
heading(text, level) {
return `
<a name="${text.replace(/\s/g, "-")}" data-orig-text="${text}" class="toc-anchor toc-anchor-h${level}"></a>
<h${level}>
${text}
</h${level}>
`
}
}

35
libs/njkRenderer.js Normal file
View File

@ -0,0 +1,35 @@
import fs from 'fs'
import njk from 'nunjucks'
import { JSDOM } from 'jsdom'
// Load in config
const ConfigFile = fs.readFileSync('./config.json')
const Config = JSON.parse(ConfigFile)
function generateToc(dom) {
const tocAnchors = dom.window.document.querySelectorAll(".toc-anchor")
let tocLinks = []
// This basically creates a proper link for the ToC. :)))
tocAnchors.forEach(anchor => tocLinks.push(`<a href="#${anchor.name}" class="${anchor.classList[1].replace("-anchor", "")}">${anchor.getAttribute('data-orig-text')}</a>`))
return tocLinks
}
function generateContext(renderedNjk) {
const dom = new JSDOM(renderedNjk)
return {
serverName: Config.serverName,
tocLinks: generateToc(dom)
}
}
export function njkRenderer(path) {
const njkFile = fs.readFileSync(path).toString()
const renderedNjk = njk.renderString(njkFile)
const context = generateContext(renderedNjk)
return njk.renderString(njkFile, context)
}

View File

@ -1,15 +1,12 @@
import fs from 'fs'
import { njkRenderer } from './njkRenderer.js'
export function requestHandler(req, res, Config) {
const context = {
serverName: Config.serverName
}
if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}.njk`))
return res.send(njkRenderer(`./${Config.contentDir}/pages/${req.path}.njk`))
if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}.njk`, context))
return res.render(`pages/${req.path}.njk`)
if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}/index.njk`, context))
return res.render(`pages/${req.path}/index.njk`)
if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}/index.njk`))
return res.send(njkRenderer(`./${Config.contentDir}/pages/${req.path}/index.njk`))
return res.status(404).render('errors/404.njk', context)
}

View File

@ -20,9 +20,9 @@
"chokidar": "^3.5.2",
"express": "^4.17.1",
"highlight.js": "^11.2.0",
"jsdom": "^16.7.0",
"marked": "^2.1.3",
"nunjucks": "^3.2.3",
"nunjucks-append": "^0.0.3",
"nunjucks-markdown": "^2.0.1"
}
}