diff --git a/assets/css/article.css b/assets/css/article.css new file mode 100644 index 0000000..cbec30d --- /dev/null +++ b/assets/css/article.css @@ -0,0 +1,75 @@ +article { + display: grid; + grid-template-areas: 'header' 'toc' 'content'; + grid-template-columns: 100%; + grid-template-rows: repeat(3, auto); +} + +article > header { + grid-area: header; + margin-bottom: 2rem; +} + +article > header > i { + color: var(--accent); + display: block; +} + +.toc-container { + grid-area: toc; + display: grid; + place-items: center; + + width: fit-content; + + margin: 0 auto; + padding: 1rem 4rem; + background-color: var(--grey3); + border-radius: .5rem; + box-shadow: 0 1rem 2rem var(--grey1); +} + +.toc-container > .toc { + width: max-content; + display: grid; +} + +.toc > * { + width: 100%; + display: block; +} + +.toc-title { + margin-top: 0; + font-size: 1.25rem; + color: var(--primary); +} + +.toc-h2 { + margin-left: .5rem; + font-size: .8rem; +} + +.toc-h3 { + margin-left: 1rem; + font-size: .75rem; +} + +.toc-h4 { + margin-left: 1.5rem; + font-size: .7rem; +} + +.toc-h5 { + margin-left: 2rem; + font-size: .65rem; +} + +.toc-h6 { + margin-left: 2.5rem; + font-size: .6rem; +} + +article > .content { + grid-area: content; +} diff --git a/assets/css/colors.css b/assets/css/colors.css new file mode 100644 index 0000000..b934a6a --- /dev/null +++ b/assets/css/colors.css @@ -0,0 +1,109 @@ +:root { + /* COLORS */ + --red: #EF5255; + --red1: #BF3638; + --red2: #D5484A; + --red3: #EF5255; + --red4: #FA7A7C; + --red5: #FA999A; + + --orange: #EB801B; + --orange1: #AF6118; + --orange2: #DA7516; + --orange3: #EB801B; + --orange4: #F99434; + --orange5: #F9AE67; + + --yellow: #F4C025; + --yellow1: #BD920F; + --yellow2: #DFAC11; + --yellow3: #F4C025; + --yellow4: #FFD147; + --yellow5: #FFE38F; + + --pear: #AED651; + --pear1: #7D9A37; + --pear2: #96BB3E; + --pear3: #AED651; + --pear4: #C2E571; + --pear5: #DBF1A7; + + --green: #72D661; + --green1: #53A545; + --green2: #61C350; + --green3: #72D661; + --green4: #93E684; + --green5: #BCF0B2; + + --sea: #3DDF89; + --sea1: #2AA764; + --sea2: #2ACB75; + --sea3: #3DDF89; + --sea4: #5DEEA1; + --sea5: #A9F4CC; + + --cyan: #4CDCDF; + --cyan1: #2CA3A5; + --cyan2: #2DCACD; + --cyan3: #4CDCDF; + --cyan4: #6BE8EB; + --cyan5: #B7EFF0; + + --blue: #4C9CEF; + --blue1: #1C67B5; + --blue2: #2A83DF; + --blue3: #4C9CEF; + --blue4: #74B6FB; + --blue5: #A3CEFA; + + --indigo: #4C6DEF; + --indigo1: #2143CA; + --indigo2: #3457E5; + --indigo3: #4C6DEF; + --indigo4: #748FFB; + --indigo5: #B1C0FC; + + --purple: #967CF4; + --purple1: #613FD9; + --purple2: #7C5DE9; + --purple3: #967CF4; + --purple4: #BCAAFD; + --purple5: #CFC2FE; + + --magenta: #E175DB; + --magenta1: #B63AB0; + --magenta2: #D152CB; + --magenta3: #E175DB; + --magenta4: #EC9AEF; + --magenta5: #EFB1F1; + + --pink: #EE588F; + --pink1: #D0356D; + --pink2: #E2407C; + --pink3: #EE588F; + --pink4: #FA80AC; + --pink5: #FBA7C6; + + --grey1: #00001F; + --grey2: #10102F; + --grey3: #20203F; + --grey4: #30304F; + --grey5: #40405F; + --grey6: #50506F; + --grey7: #60607F; + --grey8: #70708F; + --grey9: #80809F; + --grey10: #9090AF; + --grey11: #A0A0BF; + --grey12: #B0B0CF; + --grey13: #C0C0DF; + --grey14: #D0D0EF; + --grey15: #E0E0FF; + + /* ALIASES */ + --primary: var(--sea); + --accent: var(--cyan); + --accent2: var(--blue); + --surface: var(--grey2); + --text: var(--grey15); +} \ No newline at end of file diff --git a/assets/css/scaling.css b/assets/css/scaling.css new file mode 100644 index 0000000..07e45cb --- /dev/null +++ b/assets/css/scaling.css @@ -0,0 +1,66 @@ +@media screen and (min-width: 1px) { + :root { + font-size: 12px; + } + +} +@media screen and (min-width: 300px) { + :root { + font-size: 13pt; + } +} +@media screen and (min-width: 400px) { + :root { + font-size: 14pt; + } +} +@media screen and (min-width: 500px) { + :root { + font-size: 15pt; + } +} +@media screen and (min-width: 600px) { + :root { + font-size: 16pt; + } +} +@media screen and (min-width: 700px) { + :root { + font-size: 17pt; + } +} +@media screen and (min-width: 802px) { + :root { + font-size: 12pt; + } +} +@media screen and (min-width: 1000px) { + :root { + font-size: 13pt; + } +} +@media screen and (min-width: 1200px) { + :root { + font-size: 14pt; + } +} +@media screen and (min-width: 1500px) { + :root { + font-size: 15pt; + } +} +@media screen and (min-width: 1800px) { + :root { + font-size: 16pt; + } +} +@media screen and (min-width: 2500px) { + :root { + font-size: 17pt; + } +} +@media screen and (min-width: 3000px) { + :root { + font-size: 19pt; + } +} diff --git a/assets/css/sitemap.css b/assets/css/sitemap.css new file mode 100644 index 0000000..0ef2bd5 --- /dev/null +++ b/assets/css/sitemap.css @@ -0,0 +1,22 @@ +body { + display: flex; + flex-direction: column; +} + +.sitemap-container { + display: grid; + place-items: center; + + width: fit-content; + + margin: 2rem auto; + padding: 1rem 4rem; + background-color: var(--grey3); + border-radius: .5rem; + box-shadow: 0 1rem 2rem var(--grey1); +} + +.sitemap-dir > .sitemap-dir { + margin: 0 auto; + padding: 0 0 .5rem 1rem; +} \ No newline at end of file diff --git a/assets/css/syntax.css b/assets/css/syntax.css new file mode 100644 index 0000000..0f7eef0 --- /dev/null +++ b/assets/css/syntax.css @@ -0,0 +1,152 @@ +.hljs-keyword { + color: var(--purple); +} + +.hljs-built_in { + color: var(--green); +} + +.hljs-type { + color: var(--yellow); +} + +.hljs-literal { + color: var(--orange); +} + +.hljs-number { + color: var(--blue); +} + +.hljs-punctuation { + color: var(--grey14); +} + +.hljs-property { + color: var(--cyan); +} + +.hljs-regexp { + color: var(--red); +} + +.hljs-string { + color: var(--yellow); +} + +.hljs-char.escape { + color: var(--cyan); +} + +.hljs-subst { + color: var(--text); +} + +.hljs-symbol { + color: var(--blue); +} + +.hljs-variable { + color: var(--orange); +} + +.hljs-variable.language { + color: var(--yellow); +} + +.hljs-variable.constant { + color: var(--red); +} + +.hljs-title { + color: var(--red); +} + +.hljs-title.class { + color: var(--cyan); +} + +.hljs-title.class.inherited { + color: var(--purple); +} + +.hljs-title.function { + color: var(--blue); +} + +.hljs-params { + color: var(--green); +} + +.hljs-comment { + color: var(--grey9); + font-style: italic; +} + +.hljs-doctag { + color: var(--yellow); +} + +.hljs-meta { + color: var(--cyan); +} + +.hljs-meta .hljs-keyword { + color: var(--indigo); +} + +.hljs-section { + color: var(--green); +} + +.hljs-tag { + color: var(--red); +} + +.hljs-name { + color: var(--blue); +} + +.hljs-attr { + color: var(--orange); +} + +.hljs-attribute { + color: var(--green); +} + +.hljs-selector-tag { + color: var(--red); +} + +.hljs-selector-id { + color: var(--blue); +} + +.hljs-selector-class { + color: var(--orange); +} + +.hljs-selector-attr { + color: var(--purple); +} + +.hljs-selector-pseudo { + color: var(--green); +} + +.hljs-template-tag { + color: var(--purple); +} + +.hljs-template-variable { + color: var(--cyan); +} + +.hljs-addition { + color: var(--green); +} + +.hljs-deletion { + color: var(--red); +} \ No newline at end of file diff --git a/assets/css/theme.css b/assets/css/theme.css new file mode 100644 index 0000000..3b286b2 --- /dev/null +++ b/assets/css/theme.css @@ -0,0 +1,149 @@ +* { + margin: 0; + padding: 0; + border: 0 none transparent; + + box-sizing: border-box; + scroll-behavior: smooth; + + font-family: 'sans-serif'; + font-size: .85rem; + line-height: 1.5; + + color: var(--text); +} + +html { + display: grid; + place-items: center; + width: 100%; + background-color: var(--surface); +} + +body { + margin: 5vh 0 10vh 0; + width: min(95vw, 75ch); +} + +header { + color: var(--primary); + font-size: 3rem; + text-align: center; +} + +h1, h2, h3, h4, h5, h6 { + color: var(--accent); + font-weight: normal; + + margin: 1.25em 0 .25em 0; +} + +h1 { + color: var(--primary); + font-size: 2rem; +} + +h2 { + font-size: 1.65rem; +} + +h3 { + font-size: 1.35rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: .85rem; +} + +h6 { + font-size: .75rem; +} + +p { + margin: .5em 0 .25em 0; + text-align: justify; +} + +a { + color: var(--accent2); +} + +blockquote { + margin: 1rem; + padding: .5rem 1rem; + + border-left: .2rem solid var(--primary); + background-color: var(--grey3); +} + +ul, ol { + margin: 1rem 0 1rem 2rem; +} + +ul ul, ul ol, ol ol, ol ul { + margin: .25rem 0 .25rem 1rem; +} + +table { + display: block; + width: 100%; + overflow-x: auto; + white-space: nowrap; + border-collapse: collapse; +} + +table tbody { + display: table; + width: 100%; +} + +tr { + width: 200%; + background-color: var(--grey3); +} + +tr:nth-child(even) { + background-color: var(--grey4); +} + +th { + border-bottom: .1rem solid var(--text); +} + +th, td { + text-align: left; + padding: .25rem 1rem; +} + +code { + background-color: var(--grey3); + font-family: monospace; + padding: .1rem .2rem; + margin: 0 .1rem; + border-radius: .2rem; +} + +pre > code { + margin: 1rem 0; + padding: 1rem; + border-radius: .5rem; + box-shadow: 0 1rem 2rem var(--grey1); + + display: block; + overflow-x: auto; + white-space: nowrap; +} + +code * { + font-family: monospace; +} + +img { + margin: 1rem 0; + border-radius: .5rem; + box-shadow: 0 1rem 2rem var(--grey1); +} \ No newline at end of file diff --git a/assets/images/design-res-promo.svg b/assets/images/design-res-promo.svg new file mode 100644 index 0000000..8e26ed9 --- /dev/null +++ b/assets/images/design-res-promo.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config.json b/config.json new file mode 100644 index 0000000..2ef460a --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "contentDir": "content", + "assetsDir": "assets", + + "serverPort": "8789", + "serverName": "qwik" +} \ No newline at end of file diff --git a/content/components/code.njk b/content/components/code.njk new file mode 100644 index 0000000..495f139 --- /dev/null +++ b/content/components/code.njk @@ -0,0 +1,8 @@ +{% macro import(path, lang) %} +```{{ lang }} +{% set code %} +{% include path %} +{% endset %} +{{ code | safe | replace(" ", "•") }} +``` +{% endmacro %} \ No newline at end of file diff --git a/content/components/toc.njk b/content/components/toc.njk new file mode 100644 index 0000000..4dd0e44 --- /dev/null +++ b/content/components/toc.njk @@ -0,0 +1,6 @@ +{% macro header(size, text) %} + + <{{ size }}> + {{ text }} + +{% endmacro %} \ No newline at end of file diff --git a/content/errors/404.njk b/content/errors/404.njk new file mode 100644 index 0000000..93e56b5 --- /dev/null +++ b/content/errors/404.njk @@ -0,0 +1,15 @@ + + + + {% include "templates/defaultTags.njk" %} + {{ serverName }} - Error 404 + + +

Error 404

+

+ The page you are looking for doesn't exist.
+ Consider going to the main page.
+ {{ serverName }} +

+ + \ No newline at end of file diff --git a/content/pages/docs/intro.md b/content/pages/docs/intro.md new file mode 100644 index 0000000..37e1f59 --- /dev/null +++ b/content/pages/docs/intro.md @@ -0,0 +1,97 @@ +%%- +title: qwik cms +header: qwik cms intro +date: 8th August 2021 +-%% + +# Introduction +BurnyLlama started working on this project as a part of [qwik](https://qwik.space). +We tried to find a good and simple CMS solution, but we couldn't really find something that +worked for and suited us. We decided to make our own, and that's when the `qwik-cms` repo was created. + +## Basic ideas +We wanted to keep it simple, yet powerful. At first we only wanted to use *include/import statements*, +but later we came to the realization that you could add other QoL things, like automatically generating +a table of contents for articles. + +We also want to make development of content be as fast as possible. Especially with when it comes to +writing articles. Writing articles in markdown can feel kinda nice as it (at least imo) saves a lot +of time compared to writing out HTML. + +## Underlying technologies +Thanks to all the technologies used, the CMS is really hackable and can be used in different ways +depending on your liking. There are three main ways of creating content: +1. Normal HTML +2. Nunjacks (an HTML templating language) +3. Markdown + + +## Views on bloat +*This all sounds kinda bloated?* +I hear you asking... and yes, I am afraid this is a bit bloated (sadly). Although I wouldn't call it +bloated if you actually use the features. The project does pull in a few dependencies, and making the +system more modular could potentially make som of those dependencies optional, but as of now we +depend on: +* Express - the server framework +* nunjucks - the templating language that does very heavy lifting in this CMS. +* nunjucks-markdown - adds support for markdown in nunjucks +* marked - the actual renderer for the markdown (used by `nunjucks-markdown`) +* highlight.js - provides syntax highlighting for codeblocks in markdown. +* jsdom - makes us able to use DOM operations server-side (used to generate the table of contents) +* chokidar - used to reload nunjucks templates if they change on the disk (without having to restart the server) + +So yes, we sure do have a lot of dependencies, but they are all used, so there are no dependencies just "lying around". + +# Installation +The installation process will be quite trivial if you are used to node-based projects. + +## Prerequesites +We will assume that you have `node` and `npm` installed already. Otherwise make sure to install those. + +## Process +The process is really simple: +```bash +git clone https://git.qwik.space/BurnyLlama/qwik-cms +cd qwik-cms +npm i +node index.js +``` +`npm i` installs all needed dependencies. + +# Configuration +The CMS can be configured from `config.json` and the default config looks like this: +```json +{ + "contentDir": "content", + "assetsDir": "assets", + + "serverPort": "8789", + "serverName": "qwik" +} +``` + +## Explanation +All of the options are kind of self-explanatory, but anyways: + +### contentDir +Specifies which directory the content is in. + +Default: `content` + +### assetsDir +Specifies which directory the assets are in. These items will be statically available on (your site's) +`/assets/`. I recommend storing CSS, images, etc. here. + +Default: `assets` + +### serverPort +The port the server should run on. + +Default: `8789` + +### serverName +This can be accessed in any page/template by using `{{ serverName }}` + +Default: `qwik` + +# Usage diff --git a/content/pages/sitemap.njk b/content/pages/sitemap.njk new file mode 100644 index 0000000..9bdf417 --- /dev/null +++ b/content/pages/sitemap.njk @@ -0,0 +1,16 @@ + + + + {% include "templates/defaultTags.njk" %} + + Site Map + + +
+ Sitemap - "/" +
+
+ {{ siteMap() | safe }} +
+ + \ No newline at end of file diff --git a/content/templates/article.njk b/content/templates/article.njk new file mode 100644 index 0000000..7294c78 --- /dev/null +++ b/content/templates/article.njk @@ -0,0 +1,30 @@ + + + + {% include "./defaultTags.njk" %} + + + {% block head %} + {{ serverName }} + {% endblock %} + + +
+
+ {% block header %}{% endblock %} +
+ +
+
+

Table of Contents

+ {% for tocLink in tocLinks %} + {{ tocLink | safe }} + {% endfor %} + +
+
+ + {% block main %}{% endblock %} +
+ + \ No newline at end of file diff --git a/content/templates/article_md.njk b/content/templates/article_md.njk new file mode 100644 index 0000000..ccec1f9 --- /dev/null +++ b/content/templates/article_md.njk @@ -0,0 +1,31 @@ + + + + {% include "./defaultTags.njk" %} + + + {% block head %} + {{ serverName }} + {% endblock %} + + +
+
+ {% block header %}{% endblock %} +
+ +
+
+

Table of Contents

+ {% for tocLink in tocLinks %} + {{ tocLink | safe }} + {% endfor %} +
+
+ + {% markdown %} + {% block main %}{% endblock %} + {% endmarkdown %} +
+ + \ No newline at end of file diff --git a/content/templates/defaultTags.njk b/content/templates/defaultTags.njk new file mode 100644 index 0000000..af78dfe --- /dev/null +++ b/content/templates/defaultTags.njk @@ -0,0 +1,6 @@ + + + + + + diff --git a/content/templates/external_md.njk b/content/templates/external_md.njk new file mode 100644 index 0000000..0d71aaf --- /dev/null +++ b/content/templates/external_md.njk @@ -0,0 +1,30 @@ + + + + {% include "templates/defaultTags.njk" %} + + + {{ externalMeta.title if externalMeta.title else serverName }} + + +
+
+ {{ externalMeta.header }} + {{ externalMeta.date }} +
+ +
+
+

Table of Contents

+ {% for tocLink in tocLinks %} + {{ tocLink | safe }} + {% endfor %} +
+
+ + {% markdown %} + {{ externalMarkdown | safe }} + {% endmarkdown %} +
+ + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..b49297a --- /dev/null +++ b/index.js @@ -0,0 +1,44 @@ +import fs from 'fs' +import express from 'express' + +import njk from 'nunjucks' + +import { requestHandler } from './libs/requestHandler.js' +import { utils } from './libs/utils/utils.js' +import { nunjacksConfig } from './libs/nunjucksConfig.js' + + +// Load in config +const ConfigFile = fs.readFileSync('./config.json') +const Config = JSON.parse(ConfigFile) + + + +// Create a server object +const Server = express() + + + +// Configure the assets directory +Server.use('/assets', express.static(Config.assetsDir)) + +// Configure nunjacks +nunjacksConfig(njk, Server, Config) + +// Generate utils +utils.generate() + + + +// Send all requests to the requestHandler. +Server.get('*', (req, res) => requestHandler(req, res, Config)) + + + +// Start the server +Server.listen( + Config.serverPort, + () => { + console.log(`Started server on ${Config.serverPort}.`) + } +) \ No newline at end of file diff --git a/libs/codeHighlighter.js b/libs/codeHighlighter.js new file mode 100644 index 0000000..7b1a51a --- /dev/null +++ b/libs/codeHighlighter.js @@ -0,0 +1,16 @@ +import hljs from 'highlight.js' + +export function highlight(code, lang) { + // Check if hljs recognises the language, else assume 'plaintext'. + const language = hljs.getLanguage(lang) ? lang : 'plaintext' + + // Highlight it with the corresponding language. + return hljs.highlight( + // Switch out bullet points (•) to spaces (for proper indentation). + code.replace(/•/g, " "), { language }) + // Replace newlines with
tags. + .value.replace(/\n/g, "
") + // Replace spaces with ' ' to forcefully render them. + .replace(/ /g, " " + ) +} \ No newline at end of file diff --git a/libs/externalContext.js b/libs/externalContext.js new file mode 100644 index 0000000..0268290 --- /dev/null +++ b/libs/externalContext.js @@ -0,0 +1,20 @@ +export function parseExternalContext(externalContext) { + // If there's no external context, return nothing. + if (!externalContext) + return {} + + // Remove start and end tag ↓ Only care about the first "externalConext" + externalContext = externalContext[0].replace(/%%-\n|-%%\n/g, "") + + let parsedContext = {} + externalContext.split("\n").forEach(line => { + // If the line is falsey; leave. + if (!line) return + + // Assign properties to parsedContext and give them their corresponding values. + line = line.split(/:/) + parsedContext[line[0]] = line[1].replace(/^\s/, "") + }) + + return parsedContext +} \ No newline at end of file diff --git a/libs/generateContext.js b/libs/generateContext.js new file mode 100644 index 0000000..a8b8d18 --- /dev/null +++ b/libs/generateContext.js @@ -0,0 +1,30 @@ +import fs from 'fs' +import { JSDOM } from 'jsdom' +import { parseExternalContext } from './externalContext.js' + +const ConfigFile = fs.readFileSync('./config.json') +const Config = JSON.parse(ConfigFile) + +function generateToc(dom) { + // Get all ToC anchors from the DOM + const tocAnchors = dom.window.document.querySelectorAll(".toc-anchor") + + // Generate links to lead to those anvhors. + let tocLinks = [] + tocAnchors.forEach(anchor => tocLinks.push(`${anchor.getAttribute('data-orig-text')}`)) + + return tocLinks +} + +export function generateContext(prerenderedNjk, externalContext = undefined) { + // Create a 'virtual DOM' for analysis. + const dom = new JSDOM(prerenderedNjk) + + // Generate respective parts of the context. + return { + serverName: Config.serverName, + tocLinks: generateToc(dom), + externalMeta: externalContext ? parseExternalContext(externalContext) : undefined + } +} + diff --git a/libs/markedRenderer.js b/libs/markedRenderer.js new file mode 100644 index 0000000..a19789b --- /dev/null +++ b/libs/markedRenderer.js @@ -0,0 +1,22 @@ +import { highlight } from "./codeHighlighter.js" + +export const markedRenderer = { + // Rendering of headings (add an anchor above all headings). + heading(text, level) { + return ` + + + ${text} + + ` + }, + + // Render code properly, and syntax highlight it. + code(code, lang, escaped) { + return ` +
+                ${highlight(code, lang)}
+            
+ ` + } +} \ No newline at end of file diff --git a/libs/nunjucksConfig.js b/libs/nunjucksConfig.js new file mode 100644 index 0000000..d313d63 --- /dev/null +++ b/libs/nunjucksConfig.js @@ -0,0 +1,31 @@ +import njkMarkdown from 'nunjucks-markdown' +import marked from 'marked' + +import { markedRenderer } from './markedRenderer.js' +import { utils } from './utils/utils.js' + + +export function nunjacksConfig(njk, express, Config) { + // Configure the nunjucks environment (HTML templating) + const njkEnv = njk.configure( + Config.contentDir, + { + autoescape: true, // Automatically escape HTML aka a '<' will become '<' + watch: true, // Automatically reload all templates if they change on disk. + trimBlocks: true, // Remove trailing newlines from tags + lstripBlocks: true, // Automatically remove leading whitespace from tags + express: express // Use the express server. (Currently obsolete, I think.) + } + ) + + njkEnv.addGlobal('siteMap', entrypoint => utils.values.siteMap.render(entrypoint ? entrypoint : "/")) + + // Configure marked (markdown parser) + marked.use({ + gfm: true, // Use GitHub formatting? + renderer: markedRenderer // Use my own custom rendering for som tags + }) + + // Let nunjucks use markdown. + njkMarkdown.register(njkEnv, marked) +} \ No newline at end of file diff --git a/libs/requestHandler.js b/libs/requestHandler.js new file mode 100644 index 0000000..4cb0d58 --- /dev/null +++ b/libs/requestHandler.js @@ -0,0 +1,21 @@ +import fs from 'fs' +import { mdRenderer, njkRenderer } from './siteRenderer.js' + + +// Handle all request and try to find a corresponding file/template. +export function requestHandler(req, res, Config) { + // Check for njk files first + 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}/index.njk`)) + return res.send(njkRenderer(`./${Config.contentDir}/pages/${req.path}/index.njk`)) + + // Secondly search for markdown + if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}.md`)) + return res.send(mdRenderer(`./${Config.contentDir}/pages/${req.path}.md`)) + if (fs.existsSync(`./${Config.contentDir}/pages/${req.path}/index.md`)) + return res.send(mdRenderer(`./${Config.contentDir}/pages/${req.path}/index.md`)) + + // If no matching file is found, return a 404 error. + return res.status(404).send(njkRenderer(`./${Config.contentDir}/errors/404.njk`)) +} \ No newline at end of file diff --git a/libs/siteRenderer.js b/libs/siteRenderer.js new file mode 100644 index 0000000..984fc01 --- /dev/null +++ b/libs/siteRenderer.js @@ -0,0 +1,36 @@ +import fs from 'fs' +import njk from 'nunjucks' +import { generateContext } from './generateContext.js' + +// Load in config +const ConfigFile = fs.readFileSync('./config.json') +const Config = JSON.parse(ConfigFile) + +export function njkRenderer(path) { + // Read in the template + const njkFile = fs.readFileSync(path).toString() + // Pre-render it for analysis while rendering context + const prerenderedNjk = njk.renderString(njkFile) + + // Generate proper context and make a final render of the template. + const context = generateContext(prerenderedNjk) + return njk.renderString(njkFile, context) +} + +export function mdRenderer(path) { + // Load in the njk template and markdown + const njkFile = fs.readFileSync(`${Config.contentDir}/templates/external_md.njk`).toString() + const externalMarkdownFile = fs.readFileSync(path).toString() + + // Separate the actual markdown from potential 'externalContext' + const externalMarkdown = externalMarkdownFile.replace(/^%%-\n[\S\s]*-%%\n/, "").replace(/```\w*[\s\S]*?```/g, match => match.replace(/ /g, "•")) + const externalContext = externalMarkdownFile.match(/^%%-\n[\S\s]*-%%\n/) + + // Pre-render the template for analysis during context generation. + const prerenderedNjk = njk.renderString(njkFile, { externalMarkdown }) + + // Generate the context and add the externalMarkdown to the context, then render the template. + let context = generateContext(prerenderedNjk, externalContext) + context.externalMarkdown = externalMarkdown + return njk.renderString(njkFile, context) +} \ No newline at end of file diff --git a/libs/utils/siteMap.js b/libs/utils/siteMap.js new file mode 100644 index 0000000..270a80e --- /dev/null +++ b/libs/utils/siteMap.js @@ -0,0 +1,70 @@ +import fs from 'fs/promises' +import path from 'path' + +async function handlePath(dirName, siteMap) { + // Get all directories in the 'dirName' + const dirs = await fs.readdir(dirName) + + // For each file in the directory: check if it is a + // file or a folder, if a folder recursively run this + // function in that folder. + for (const dirIndex in dirs) { + const file = dirs[dirIndex] + const fullPath = path.join(dirName, file) + const fileStats = await fs.stat(fullPath) + + if (fileStats.isDirectory()) + siteMap[fullPath.replace(/^[\s\S]+\/pages/, "")] = { + path: fullPath, + address: fullPath.replace(/^[\s\S]+\/pages/, ""), + type: "directory", + children: await handlePath(fullPath, {}) + } + else + siteMap[fullPath.replace(/^[\s\S]+\/pages/, "").replace(/\.[a-z]+$/, "")] = { + path: fullPath, + address: fullPath.replace(/^[\s\S]+\/pages/, "").replace(/\.[a-z]+$/, ""), + type: "file" + } + } + + return siteMap +} + +function renderSiteMap(siteMap, entrypoint) { + // Make a template for the sitemap and recursively run + // for all directories with children. + return ` +
+ ${(() => { + let result = [] + + for (const dirIndex in siteMap) { + const dir = siteMap[dirIndex] + if (!dir.address.startsWith(entrypoint)) + continue + + if (dir.type === "directory") { + result.push(`${dir.address}`) + result.push(renderSiteMap(dir.children, entrypoint)) + result.push(``) + } else { + result.push(`${dir.address}`) + } + } + + return result.join("") + })()} +
+ ` +} + +export async function generateSiteMap(Config) { + // Generate a site map and return it and a render-function. + const siteMap = await handlePath(`${Config.contentDir}/pages`, {}) + + return { + siteMap, + render: entrypoint => renderSiteMap(siteMap, entrypoint) + } +} \ No newline at end of file diff --git a/libs/utils/utils.js b/libs/utils/utils.js new file mode 100644 index 0000000..1f0dfc9 --- /dev/null +++ b/libs/utils/utils.js @@ -0,0 +1,14 @@ +import fs from 'fs' +import { generateSiteMap } from './siteMap.js' + +const ConfigFile = fs.readFileSync('./config.json') +const Config = JSON.parse(ConfigFile) + +// Generate some utilities +export let utils = { + generate: async () => + utils.values = { + siteMap: await generateSiteMap(Config) + }, + values: {} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..07aa252 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1872 @@ +{ + "name": "qwik-cms", + "version": "1.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "qwik-cms", + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "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-markdown": "^2.0.1" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, + "node_modules/abab": { + "version": "2.0.5", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + }, + "node_modules/accepts": { + "version": "1.3.7", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.4.1", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/anymatch": { + "version": "3.1.2", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asap": { + "version": "2.0.6", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.19.0", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/bytes": { + "version": "3.1.0", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/cssom": { + "version": "0.4.4", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "node_modules/deep-is": { + "version": "0.1.3", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/domexception": { + "version": "2.0.1", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escodegen": { + "version": "2.0.0", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.2.0", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "node_modules/fill-range": { + "version": "7.0.1", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/highlight.js": { + "version": "11.2.0", + "integrity": "sha512-JOySjtOEcyG8s4MLR2MNbLUyaXqUunmSnL2kdV/KuGJOmHZuAR5xC54Ko7goAXBWNhf09Vy3B+U7vR62UZ/0iw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/jsdom": { + "version": "16.7.0", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/levn": { + "version": "0.3.0", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/marked": { + "version": "2.1.3", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.48.0", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.31", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "dependencies": { + "mime-db": "1.48.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nunjucks": { + "version": "3.2.3", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks-markdown": { + "version": "2.0.1", + "integrity": "sha1-1V51Qzo1hQ4sNFZR/j+THtmxVqI=", + "peerDependencies": { + "nunjucks": "^2.3.0 || ^3.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "node_modules/on-finished": { + "version": "2.3.0", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/punycode": { + "version": "2.1.1", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.1", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/source-map": { + "version": "0.6.1", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "7.5.3", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + } + }, + "dependencies": { + "@tootallnate/once": { + "version": "1.1.2" + }, + "a-sync-waterfall": { + "version": "1.0.1" + }, + "abab": { + "version": "2.0.5" + }, + "accepts": { + "version": "1.3.7", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.4.1" + }, + "acorn-globals": { + "version": "6.0.0", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1" + } + } + }, + "acorn-walk": { + "version": "7.2.0" + }, + "agent-base": { + "version": "6.0.2", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2" + } + } + }, + "anymatch": { + "version": "3.1.2", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-flatten": { + "version": "1.1.1" + }, + "asap": { + "version": "2.0.6" + }, + "asynckit": { + "version": "0.4.0" + }, + "binary-extensions": { + "version": "2.2.0" + }, + "body-parser": { + "version": "1.19.0", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "braces": { + "version": "3.0.2", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0" + }, + "bytes": { + "version": "3.1.0" + }, + "chokidar": { + "version": "3.5.2", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "5.1.0" + }, + "content-disposition": { + "version": "0.5.3", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4" + }, + "cookie": { + "version": "0.4.0" + }, + "cookie-signature": { + "version": "1.0.6" + }, + "cssom": { + "version": "0.4.4" + }, + "cssstyle": { + "version": "2.3.0", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8" + } + } + }, + "data-urls": { + "version": "2.0.0", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "2.6.9", + "requires": { + "ms": "2.0.0" + } + }, + "decimal.js": { + "version": "10.3.1" + }, + "deep-is": { + "version": "0.1.3" + }, + "delayed-stream": { + "version": "1.0.0" + }, + "depd": { + "version": "1.1.2" + }, + "destroy": { + "version": "1.0.4" + }, + "domexception": { + "version": "2.0.1", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0" + } + } + }, + "ee-first": { + "version": "1.1.1" + }, + "encodeurl": { + "version": "1.0.2" + }, + "escape-html": { + "version": "1.0.3" + }, + "escodegen": { + "version": "2.0.0", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1" + }, + "estraverse": { + "version": "5.2.0" + }, + "esutils": { + "version": "2.0.3" + }, + "etag": { + "version": "1.8.1" + }, + "express": { + "version": "4.17.1", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "fast-levenshtein": { + "version": "2.0.6" + }, + "fill-range": { + "version": "7.0.1", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "form-data": { + "version": "3.0.1", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0" + }, + "fresh": { + "version": "0.5.2" + }, + "glob-parent": { + "version": "5.1.2", + "requires": { + "is-glob": "^4.0.1" + } + }, + "highlight.js": { + "version": "11.2.0" + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "http-errors": { + "version": "1.7.2", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2" + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3" + }, + "ipaddr.js": { + "version": "1.9.1" + }, + "is-binary-path": { + "version": "2.1.0", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1" + }, + "is-glob": { + "version": "4.0.1", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0" + }, + "is-potential-custom-element-name": { + "version": "1.0.1" + }, + "jsdom": { + "version": "16.7.0", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "levn": { + "version": "0.3.0", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.21" + }, + "marked": { + "version": "2.1.3" + }, + "media-typer": { + "version": "0.3.0" + }, + "merge-descriptors": { + "version": "1.0.1" + }, + "methods": { + "version": "1.1.2" + }, + "mime": { + "version": "1.6.0" + }, + "mime-db": { + "version": "1.48.0" + }, + "mime-types": { + "version": "2.1.31", + "requires": { + "mime-db": "1.48.0" + } + }, + "ms": { + "version": "2.0.0" + }, + "negotiator": { + "version": "0.6.2" + }, + "normalize-path": { + "version": "3.0.0" + }, + "nunjucks": { + "version": "3.2.3", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + } + }, + "nunjucks-markdown": { + "version": "2.0.1", + "requires": {} + }, + "nwsapi": { + "version": "2.2.0" + }, + "on-finished": { + "version": "2.3.0", + "requires": { + "ee-first": "1.1.1" + } + }, + "optionator": { + "version": "0.8.3", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "parse5": { + "version": "6.0.1" + }, + "parseurl": { + "version": "1.3.3" + }, + "path-to-regexp": { + "version": "0.1.7" + }, + "picomatch": { + "version": "2.3.0" + }, + "prelude-ls": { + "version": "1.1.2" + }, + "proxy-addr": { + "version": "2.0.7", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.8.0" + }, + "punycode": { + "version": "2.1.1" + }, + "qs": { + "version": "6.7.0" + }, + "range-parser": { + "version": "1.2.1" + }, + "raw-body": { + "version": "2.4.0", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readdirp": { + "version": "3.6.0", + "requires": { + "picomatch": "^2.2.1" + } + }, + "safe-buffer": { + "version": "5.1.2" + }, + "safer-buffer": { + "version": "2.1.2" + }, + "saxes": { + "version": "5.0.1", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "send": { + "version": "0.17.1", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1" + } + } + }, + "serve-static": { + "version": "1.14.1", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1" + }, + "source-map": { + "version": "0.6.1", + "optional": true + }, + "statuses": { + "version": "1.5.0" + }, + "symbol-tree": { + "version": "3.2.4" + }, + "to-regex-range": { + "version": "5.0.1", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0" + }, + "tough-cookie": { + "version": "4.0.0", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "requires": { + "punycode": "^2.1.1" + } + }, + "type-check": { + "version": "0.3.2", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "universalify": { + "version": "0.1.2" + }, + "unpipe": { + "version": "1.0.0" + }, + "utils-merge": { + "version": "1.0.1" + }, + "vary": { + "version": "1.1.2" + }, + "w3c-hr-time": { + "version": "1.0.2", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0" + }, + "whatwg-encoding": { + "version": "1.0.5", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0" + }, + "whatwg-url": { + "version": "8.7.0", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "word-wrap": { + "version": "1.2.3" + }, + "ws": { + "version": "7.5.3", + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0" + }, + "xmlchars": { + "version": "2.2.0" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..59cb01a --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "qwik-cms", + "version": "1.2.0", + "description": "A dead simple CMS. No bullshit.", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://git.qwik.space/BurnyLlama/qwik-cms" + }, + "keywords": [ + "cms" + ], + "author": "BurnyLlama", + "license": "MIT", + "dependencies": { + "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-markdown": "^2.0.1" + } +}