diff options
| -rw-r--r-- | .eslintignore | 2 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | app/constants.mjs | 9 | ||||
| -rw-r--r-- | app/directory.mjs | 116 | ||||
| -rw-r--r-- | app/directory.ts | 210 | ||||
| -rw-r--r-- | app/index.ts (renamed from app/index.mjs) | 54 | ||||
| -rw-r--r-- | app/wikiparser.mjs | 7 | ||||
| -rw-r--r-- | package-lock.json | 193 | ||||
| -rw-r--r-- | package.json | 5 | ||||
| -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.json | 11 | ||||
| -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/* @@ -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, '"')); 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 |
