aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintignore2
-rw-r--r--.gitignore1
-rw-r--r--app/constants.mjs9
-rw-r--r--app/directory.mjs116
-rw-r--r--app/directory.ts210
-rw-r--r--app/index.ts (renamed from app/index.mjs)54
-rw-r--r--app/wikiparser.mjs7
-rw-r--r--package-lock.json193
-rw-r--r--package.json5
-rw-r--r--pages/template/blog.wiki (renamed from pages/templates/blog.wiki)0
-rw-r--r--static/css/globalstyles.css (renamed from app/static/css/globalstyles.css)0
-rw-r--r--static/scripts/purge.js (renamed from app/static/scripts/purge.js)0
-rw-r--r--static/scripts/rebuild.js (renamed from app/static/scripts/rebuild.js)0
-rw-r--r--tsconfig.json11
-rw-r--r--views/error.ejs (renamed from app/views/error.ejs)0
-rw-r--r--views/index.ejs (renamed from app/views/index.ejs)0
-rw-r--r--views/page.ejs (renamed from app/views/page.ejs)0
-rw-r--r--views/partials/header.ejs (renamed from app/views/partials/header.ejs)0
-rw-r--r--views/partials/navbar.ejs (renamed from app/views/partials/navbar.ejs)0
-rw-r--r--views/purge.ejs (renamed from app/views/purge.ejs)0
-rw-r--r--views/rebuild.ejs (renamed from app/views/rebuild.ejs)0
21 files changed, 435 insertions, 173 deletions
diff --git a/.eslintignore b/.eslintignore
index 8ec4cb0..3eaeff0 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,2 @@
-app/static/*
+static/*
node_modules/*
diff --git a/.gitignore b/.gitignore
index 713d500..17c578e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
node_modules/
+build/
.env
diff --git a/app/constants.mjs b/app/constants.mjs
deleted file mode 100644
index ace7e2f..0000000
--- a/app/constants.mjs
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-export const SERVER_PORT = 3000;
-export const PARSER_MAX_RECURSION = 20;
-export const PURGE_COOLDOWN_MIN = -1;
-export const REBUILD_COOLDOWN_MIN = -1;
-export const PAGES_DIR = 'pages';
-export const TEMPLATE_DIR = 'pages/tempates';
-export const IMAGES_DIR = 'pages/images';
diff --git a/app/directory.mjs b/app/directory.mjs
deleted file mode 100644
index b470bc7..0000000
--- a/app/directory.mjs
+++ /dev/null
@@ -1,116 +0,0 @@
-'use strict';
-
-import { PAGES_DIR, PURGE_COOLDOWN_MIN, REBUILD_COOLDOWN_MIN } from './constants.mjs';
-import { parse } from './wikiparser.mjs';
-import { readFileSync, readdirSync } from 'fs';
-
-const pages = {};
-const metadata = {};
-
-export function pageFor(path) {
- path = path.replace(/[^a-z0-9]/gi, '_').toLowerCase();
- let page = pages[path];
- if (!page) {
- return undefined;
- }
-
- if (!page.html) {
- buildPage(path);
- return pages[path];
- }
-
- return page;
-}
-
-function buildPage(path) {
- let data;
- try {
- data = readFileSync(`${PAGES_DIR}/${path}.wiki`, 'utf-8');
- } catch {
- return false;
- }
- let result = parse(data);
- let title = result.metadata.displayTitle ?? 'Unnamed page';
- let content = `${result.metadata.notitle ? '' : `<h1>${title}</h1>`}${result.html}`;
-
- let page = {
- html: content,
- raw: data,
- buildTime: result.metadata.buildTime,
- primary: result.metadata.primary ?? false,
- sortOrder: result.metadata.sortOrder ?? -1,
- notitle: result.metadata.notitle ?? false,
- displayTitle: title
- };
- pages[path] = page;
- return true;
-}
-
-export function rebuild() {
- if (metadata.fileTreeBuildTime + REBUILD_COOLDOWN_MIN * 60 * 1000 > Date.now()) {
- return false;
- }
- for (var page in pages) {
- delete pages[page];
- }
-
- readdirSync(PAGES_DIR).forEach(file => {
- if (!file.endsWith('.wiki')) {
- return;
- }
- file = file.replace('.wiki', '');
- buildPage(file);
- });
-
- let primaryPages = [];
- for (const page of Object.keys(pages)) {
- if (pages[page].primary) {
- primaryPages.push(page);
- }
- }
- primaryPages.sort((a, b) => {
- return pages[a].sortOrder - pages[b].sortOrder;
- });
- metadata.navbar = primaryPages;
- metadata.fileTreeBuildTime = new Date();
- return true;
-}
-
-export function exists(path) {
- return !!pages[path];
-}
-
-export function rawDataFor(path) {
- return pages[path];
-}
-
-export function purge(path) {
- let page = pages[path];
- if (page) {
- if (page.buildTime.getTime() + PURGE_COOLDOWN_MIN * 60 * 1000 > Date.now()) {
- return false;
- } else {
- pages[path] = {};
- if (buildPage(path)) {
- return true;
- }
- delete pages[path];
- }
- }
- return false;
-}
-
-export function getPages() {
- return pages;
-}
-
-export function getNavbar(current = '') {
- if (!metadata.navbar) {
- return '';
- }
- let navbar = '';
- for (const page of metadata.navbar) {
- navbar = navbar + `<div class="navbar-element"><a href="/${page}"${current == page ? ' class="highlight"' : ''}>${pages[page].displayTitle}</a></div>`;
- }
- return navbar;
-}
diff --git a/app/directory.ts b/app/directory.ts
new file mode 100644
index 0000000..6449e8e
--- /dev/null
+++ b/app/directory.ts
@@ -0,0 +1,210 @@
+'use strict';
+
+import { parse } from './wikiparser.mjs';
+import { readFileSync, readdirSync, statSync } from 'fs';
+import glob from 'glob';
+
+export class PageDirectory {
+
+ pages: Record<string, Page>;
+ primaryPages: Page[];
+ pagePath: string;
+ lastBuild: number;
+
+ constructor(root: string) {
+ this.lastBuild = 0;
+ this.pages = {};
+ this.pagePath = root;
+
+ this.rebuild();
+ }
+
+ /**
+ * Build this page directory.
+ *
+ * @returns whether the directory was built
+ */
+ rebuild(): boolean {
+ if (this.lastBuild + parseInt(process.env.REBUILD_COOLDOWN_MIN, 10) * 60 * 1000 > Date.now()) {
+ return false;
+ }
+ for (var page in this.pages) {
+ delete this.pages[page];
+ }
+
+ let pages = glob.sync(`**/*.wiki`, { cwd: this.pagePath })
+
+ pages.forEach(page => {
+ page = page.replace('.wiki', '').replace('/', ':').replace(/[^a-z0-9:]/gi, '_').toLowerCase();
+ this.pages[page] = this.buildPage(page);
+ });
+
+ let primaryPages = [];
+ Object.entries(this.pages).forEach(([name, page]) => {
+ if (page.metadata.includeInNavbar) {
+ primaryPages.push(page);
+ }
+ });
+
+ primaryPages.sort((a, b) => {
+ return a.metadata.sortOrder - b.metadata.sortOrder;
+ });
+ this.primaryPages = primaryPages;
+ this.lastBuild = Date.now();
+ return true;
+ }
+
+ /**
+ * Get whether a page exists with this name.
+ *
+ * @param name standard name for page
+ * @returns whether the page exists
+ */
+ exists(name: string): boolean {
+ return !!this.pages[this.convertNameToStandard(name)];
+ }
+
+ /**
+ * Get a page.
+ *
+ * @param name standard name for page
+ * @returns page
+ */
+ get(name: string): Page {
+ name = this.convertNameToStandard(name);
+ let page = this.pages[name];
+ if (!page) {
+ return undefined;
+ }
+
+ if (!page.html) {
+ return this.buildPage(name)
+ }
+
+ return page;
+ }
+
+ /**
+ * Get the raw wikitext for a page.
+ *
+ * @param name standard name for page
+ * @returns raw wikitext
+ */
+ getRaw(name: string): string {
+ name = this.convertNameToStandard(name);
+ return this.pages[name]?.raw;
+ }
+
+ /**
+ * Purge (rebuild) a page.
+ *
+ * @param name standard name for page
+ * @returns whether the page was rebuilt
+ */
+ purge(name: string): boolean {
+ name = this.convertNameToStandard(name);
+ let page = this.pages[name];
+ if (page) {
+ if (page.buildTime + parseInt(process.env.PURGE_COOLDOWN_MIN, 10) * 60 * 1000 > Date.now()) {
+ return false;
+ } else {
+ delete this.pages[name];
+ if (this.buildPage(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get all pages.
+ *
+ * @returns all pages
+ */
+ getPages(): Record<string, Page> {
+ return this.pages;
+ }
+
+ /**
+ * Get primary pages.
+ *
+ * @param current
+ * @returns
+ */
+ getPrimaryPages(): Page[] {
+ return this.primaryPages;
+ }
+
+ /**
+ * Build a page.
+ *
+ * @param path standard name for page
+ * @returns newly built page, or undefined
+ */
+ private buildPage(name: string): Page {
+ name = this.convertNameToStandard(name);
+ let data: string;
+ try {
+ data = readFileSync(`${this.pagePath}/${this.convertStandardToFilePath(name)}`, 'utf-8');
+ } catch {
+ return undefined;
+ }
+ let result = parse(data);
+ let title = result.metadata.displayTitle ?? name;
+ let content = `${result.metadata.notitle ? '' : `<h1>${title}</h1>`}${result.html}`;
+
+ let page: Page = {
+ html: content,
+ raw: data,
+ standardName: name,
+ buildTime: Date.now(),
+ metadata: {
+ includeInNavbar: result.metadata.primary ?? false,
+ sortOrder: result.metadata.sortOrder ?? -1,
+ showTitle: !result.metadata.notitle ?? true,
+ displayTitle: title
+ }
+ };
+ this.pages[name] = page;
+ return page;
+ }
+
+ /**
+ * Convert a page name to a standard name.
+ * A standard name is the key used by the page directory.
+ *
+ * @param name non-standard name for a page
+ */
+ private convertNameToStandard(name: string): string {
+ return name.replace(/[^a-z0-9:]/gi, '_').toLowerCase();
+ }
+
+ /**
+ * Convert a standard name to a file path.
+ *
+ * @param name standard name for a page
+ */
+ private convertStandardToFilePath(name: string): string {
+ let [first, second] = name.split(':');
+ let [title, subpage] = ((second) ? second : first).split('.')
+ let namespace = (second) ? first : undefined
+
+ return `${namespace ? `${namespace}/` : ''}${title}${subpage ? `.${subpage}` : ''}.wiki`
+ }
+};
+
+export type Page = {
+ html: string;
+ raw: string;
+ standardName: string,
+ buildTime: number;
+ metadata: PageMetadata;
+};
+
+export type PageMetadata = {
+ displayTitle?: string;
+ sortOrder?: number;
+ showTitle?: boolean;
+ includeInNavbar?: boolean;
+};
diff --git a/app/index.mjs b/app/index.ts
index 0f14ccf..9b16ec5 100644
--- a/app/index.mjs
+++ b/app/index.ts
@@ -1,38 +1,44 @@
'use strict';
-import { SERVER_PORT } from './constants.mjs';
-import * as directory from './directory.mjs';
+import { PageDirectory, Page, PageMetadata } from './directory.js';
import express from 'express';
-import { fileURLToPath } from 'url';
-import { dirname } from 'path';
+import dotenv from 'dotenv';
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
+dotenv.config()
const app = express();
+const directory = new PageDirectory(process.env.PAGES_DIR);
directory.rebuild();
-app.use(express.static(__dirname + '/static'));
+function navbar(current: string = ''): string {
+ let navbar = '';
+ directory.primaryPages.forEach(page => {
+ navbar += `<div class="navbar-element"><a href="/${page.standardName}"${current == page.standardName ? ' class="highlight"' : ''}>${page.metadata.displayTitle}</a></div>`;
+ })
+ return navbar
+}
+
+app.use(express.static('static'));
app.set('view engine', 'ejs');
-app.set('views', __dirname + '/views');
+app.set('views', 'views');
app.get('/:page.wiki', (req, res) => {
let path = req.params.page;
- let page = directory.pageFor(path);
+ let raw = directory.getRaw(path);
- if (!page) {
+ if (!raw) {
error(res, 404);
return;
}
res.type('text/plain');
- res.send(page.raw).end();
+ res.send(raw).end();
});
app.get('/:page?', (req, res) => {
let path = req.params.page ?? 'index';
- let page = directory.pageFor(path);
+ let page = directory.get(path);
if (!page) {
error(res, 404);
@@ -40,17 +46,17 @@ app.get('/:page?', (req, res) => {
}
res.render('page.ejs', {
- navbar: directory.getNavbar(path),
+ navbar: navbar(),
path: path,
content: page.html,
- title: page.displayTitle,
- buildTime: page.buildTime.toString()
+ title: page.metadata.displayTitle,
+ buildTime: new Date(page.buildTime)
});
});
app.get('/special/purge/:page?', (req, res) => {
let path = req.params.page ?? 'index';
- let page = directory.rawDataFor(path);
+ let page = directory.get(path);
if (!page) {
error(res, 404);
@@ -58,16 +64,16 @@ app.get('/special/purge/:page?', (req, res) => {
}
res.render('purge.ejs', {
- navbar: directory.getNavbar(),
+ navbar: navbar(),
page: path,
- buildTime: page.buildTime?.toString() ?? 'never',
- buildTimeRelative: Math.round((Date.now() - page.buildTime?.getTime()) / 1000 / 60)
+ buildTime: new Date(page.buildTime) ?? 'never',
+ buildTimeRelative: Math.round((Date.now() - page.buildTime) / 1000 / 60)
});
});
app.get('/special/purge/:page/confirm', (req, res) => {
let path = req.params.page;
- let page = directory.rawDataFor(path);
+ let page = directory.get(path);
if (!page) {
error(res, 404);
@@ -83,7 +89,7 @@ app.get('/special/purge/:page/confirm', (req, res) => {
app.get('/special/rebuild', (req, res) => {
res.render('rebuild.ejs', {
- navbar: directory.getNavbar()
+ navbar: navbar()
});
});
@@ -95,13 +101,13 @@ app.get('/special/rebuild/confirm', (req, res) => {
}
});
-app.listen(SERVER_PORT, () => {
- console.log(`App listening on ${SERVER_PORT}`);
+app.listen(process.env.PORT, () => {
+ console.log(`App listening on ${process.env.PORT}`);
});
function error(res, code) {
res.render('error.ejs', {
code: code,
- navbar: directory.getNavbar()
+ navbar: navbar()
});
}
diff --git a/app/wikiparser.mjs b/app/wikiparser.mjs
index 512d985..e49a09c 100644
--- a/app/wikiparser.mjs
+++ b/app/wikiparser.mjs
@@ -18,7 +18,6 @@
// in an action of contract, negligence or other tortious action, arising out of or in
// connection with the use or performance of this software.
-import { PARSER_MAX_RECURSION, TEMPLATE_DIR, IMAGES_DIR } from './constants.mjs';
import dateFormat from 'dateformat';
import htmlEscape from 'escape-html';
import * as fs from 'fs';
@@ -40,7 +39,7 @@ export function parse(data) {
let outText = data;
- for (let l = 0, last = ''; l < PARSER_MAX_RECURSION; l++) {
+ for (let l = 0, last = ''; l < parseInt(process.env.PARSER_MAX_RECURSION, 10); l++) {
if (last === outText) break; last = outText;
outText = outText
@@ -117,7 +116,7 @@ export function parse(data) {
// Templates: {{template}}
.replace(re(r`{{ \s* ([^#}|]+?) (\|[^}]+)? }} (?!})`), (_, title, params = '') => {
if (/{{/.test(params)) return _;
- const page = TEMPLATE_DIR + '/' + title.trim().replace(/ /g, '_');
+ const page = 'Template:' + title.trim().replace(/ /g, '_');
// Retrieve template content
let content = '';
@@ -151,7 +150,7 @@ export function parse(data) {
// Images: [[File:Image.png|options|caption]]
.replace(re(r`\[\[ (?:File|Image): (.+?) (\|.+?)? \]\]`), (_, file, params) => {
if (/{{/.test(params)) return _;
- const path = IMAGES_DIR + '/' + file.trim().replace(/ /g, '_');
+ const path = 'File:' + file.trim().replace(/ /g, '_');
let caption = '';
let imageData = {};
let imageArgs = params.split('|').map((arg) => arg.replace(/"/g, '&quot;'));
diff --git a/package-lock.json b/package-lock.json
index 78ced12..526fb0a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,13 +9,16 @@
"version": "unpublished",
"license": "UNLICENSED",
"dependencies": {
+ "@types/express": "^4.17.13",
"dateformat": "^5.0.2",
"dotenv": "^10.0.0",
"ejs": "^3.1.6",
"escape-html": "^1.0.3",
- "express": "^4.17.1"
+ "express": "^4.17.1",
+ "glob": "^7.2.0"
},
"devDependencies": {
+ "@types/glob": "^7.2.0",
"eslint": "^8.2.0"
}
},
@@ -105,6 +108,89 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.26",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz",
+ "integrity": "sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "node_modules/@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
+ },
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "17.0.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz",
+ "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ=="
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -840,8 +926,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/functional-red-black-tree": {
"version": "1.0.1",
@@ -853,7 +938,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
- "dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -968,7 +1052,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -1182,7 +1265,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -1228,7 +1310,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -1604,8 +1685,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/yallist": {
"version": "4.0.0",
@@ -1683,6 +1763,89 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.26",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz",
+ "integrity": "sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==",
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
+ },
+ "@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "17.0.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz",
+ "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ=="
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+ },
+ "@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "requires": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -2249,8 +2412,7 @@
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"functional-red-black-tree": {
"version": "1.0.1",
@@ -2262,7 +2424,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
- "dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2341,7 +2502,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
@@ -2504,7 +2664,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
"requires": {
"wrappy": "1"
}
@@ -2540,8 +2699,7 @@
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-key": {
"version": "3.1.1",
@@ -2811,8 +2969,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "4.0.0",
diff --git a/package.json b/package.json
index 8443acd..9e79fda 100644
--- a/package.json
+++ b/package.json
@@ -10,14 +10,17 @@
"author": "Leonardo Bishop",
"license": "UNLICENSED",
"dependencies": {
+ "@types/express": "^4.17.13",
"dateformat": "^5.0.2",
"dotenv": "^10.0.0",
"ejs": "^3.1.6",
"escape-html": "^1.0.3",
- "express": "^4.17.1"
+ "express": "^4.17.1",
+ "glob": "^7.2.0"
},
"type": "module",
"devDependencies": {
+ "@types/glob": "^7.2.0",
"eslint": "^8.2.0"
}
}
diff --git a/pages/templates/blog.wiki b/pages/template/blog.wiki
index 92b60bd..92b60bd 100644
--- a/pages/templates/blog.wiki
+++ b/pages/template/blog.wiki
diff --git a/app/static/css/globalstyles.css b/static/css/globalstyles.css
index 774c6ca..774c6ca 100644
--- a/app/static/css/globalstyles.css
+++ b/static/css/globalstyles.css
diff --git a/app/static/scripts/purge.js b/static/scripts/purge.js
index 5ee34f0..5ee34f0 100644
--- a/app/static/scripts/purge.js
+++ b/static/scripts/purge.js
diff --git a/app/static/scripts/rebuild.js b/static/scripts/rebuild.js
index 8fd0e2e..8fd0e2e 100644
--- a/app/static/scripts/rebuild.js
+++ b/static/scripts/rebuild.js
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..76ab177
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "outDir": "./build",
+ "allowJs": true,
+ "target": "es5",
+ "module": "es2020",
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["./app/**/*.ts"],
+}
diff --git a/app/views/error.ejs b/views/error.ejs
index 88e1a27..88e1a27 100644
--- a/app/views/error.ejs
+++ b/views/error.ejs
diff --git a/app/views/index.ejs b/views/index.ejs
index f47b830..f47b830 100644
--- a/app/views/index.ejs
+++ b/views/index.ejs
diff --git a/app/views/page.ejs b/views/page.ejs
index 41ee1e2..41ee1e2 100644
--- a/app/views/page.ejs
+++ b/views/page.ejs
diff --git a/app/views/partials/header.ejs b/views/partials/header.ejs
index bd11ce0..bd11ce0 100644
--- a/app/views/partials/header.ejs
+++ b/views/partials/header.ejs
diff --git a/app/views/partials/navbar.ejs b/views/partials/navbar.ejs
index ff0c84d..ff0c84d 100644
--- a/app/views/partials/navbar.ejs
+++ b/views/partials/navbar.ejs
diff --git a/app/views/purge.ejs b/views/purge.ejs
index 04bae61..04bae61 100644
--- a/app/views/purge.ejs
+++ b/views/purge.ejs
diff --git a/app/views/rebuild.ejs b/views/rebuild.ejs
index 9f9cfaa..9f9cfaa 100644
--- a/app/views/rebuild.ejs
+++ b/views/rebuild.ejs