blah
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,2 @@
 | 
				
			|||||||
node_modules
 | 
					node_modules
 | 
				
			||||||
dist
 | 
					 | 
				
			||||||
package-lock.json
 | 
					package-lock.json
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										133
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
 | 
				
			||||||
 | 
					    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
 | 
				
			||||||
 | 
					    return new (P || (P = Promise))(function (resolve, reject) {
 | 
				
			||||||
 | 
					        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
 | 
				
			||||||
 | 
					        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
 | 
				
			||||||
 | 
					        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
 | 
				
			||||||
 | 
					        step((generator = generator.apply(thisArg, _arguments || [])).next());
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					import http from "node:http";
 | 
				
			||||||
 | 
					import { URL } from "node:url";
 | 
				
			||||||
 | 
					import querystring from "node:querystring";
 | 
				
			||||||
 | 
					import Router from "./router.js";
 | 
				
			||||||
 | 
					import Tpl from "./template.js";
 | 
				
			||||||
 | 
					export { Router, Tpl };
 | 
				
			||||||
 | 
					export default class Flummpress {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        this.router = new Router();
 | 
				
			||||||
 | 
					        this.tpl = new Tpl();
 | 
				
			||||||
 | 
					        this.middleware = [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    use(plugin) {
 | 
				
			||||||
 | 
					        if (plugin instanceof Router)
 | 
				
			||||||
 | 
					            this.router.use(plugin);
 | 
				
			||||||
 | 
					        else if (plugin instanceof Tpl)
 | 
				
			||||||
 | 
					            this.tpl = plugin;
 | 
				
			||||||
 | 
					        else if (typeof plugin === "function")
 | 
				
			||||||
 | 
					            this.middleware.push(plugin);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    processPipeline(handlers, req, res) {
 | 
				
			||||||
 | 
					        return __awaiter(this, void 0, void 0, function* () {
 | 
				
			||||||
 | 
					            for (const handler of handlers) {
 | 
				
			||||||
 | 
					                if (typeof handler !== "function")
 | 
				
			||||||
 | 
					                    throw new TypeError(`Handler is not a function: ${handler}`);
 | 
				
			||||||
 | 
					                let nextCalled = false;
 | 
				
			||||||
 | 
					                yield handler(req, res, () => nextCalled = true);
 | 
				
			||||||
 | 
					                if (!nextCalled || res.writableEnded)
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    listen(...args) {
 | 
				
			||||||
 | 
					        this.server = http.createServer((request, response) => __awaiter(this, void 0, void 0, function* () {
 | 
				
			||||||
 | 
					            var _a, _b;
 | 
				
			||||||
 | 
					            const req = this.parseRequest(request);
 | 
				
			||||||
 | 
					            const res = this.createResponse(response);
 | 
				
			||||||
 | 
					            const start = process.hrtime();
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                yield this.processPipeline(this.middleware, req, res);
 | 
				
			||||||
 | 
					                const route = this.router.getRoute(req.url.pathname, req.method);
 | 
				
			||||||
 | 
					                if (route) {
 | 
				
			||||||
 | 
					                    const [pathPattern, methods] = route;
 | 
				
			||||||
 | 
					                    const handler = methods[(_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()];
 | 
				
			||||||
 | 
					                    req.params = ((_b = req.url.pathname.match(new RegExp(pathPattern))) === null || _b === void 0 ? void 0 : _b.groups) || {};
 | 
				
			||||||
 | 
					                    req.post = yield this.readBody(req);
 | 
				
			||||||
 | 
					                    yield this.processPipeline(handler, req, res);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    res.writeHead(404).end("404 - Not Found");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (err) {
 | 
				
			||||||
 | 
					                console.error(err);
 | 
				
			||||||
 | 
					                res.writeHead(500).end("500 - Internal Server Error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            console.log([
 | 
				
			||||||
 | 
					                `[${new Date().toISOString()}]`,
 | 
				
			||||||
 | 
					                `${(process.hrtime(start)[1] / 1e6).toFixed(2)}ms`,
 | 
				
			||||||
 | 
					                `${req.method} ${res.statusCode}`,
 | 
				
			||||||
 | 
					                req.url.pathname,
 | 
				
			||||||
 | 
					            ].join(" | "));
 | 
				
			||||||
 | 
					        })).listen(...args);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    parseRequest(request) {
 | 
				
			||||||
 | 
					        const url = new URL(request.url.replace(/(?!^.)(\/+)?$/, ""), "http://localhost");
 | 
				
			||||||
 | 
					        const req = request;
 | 
				
			||||||
 | 
					        req.url = {
 | 
				
			||||||
 | 
					            pathname: url.pathname,
 | 
				
			||||||
 | 
					            split: url.pathname.split("/").slice(1),
 | 
				
			||||||
 | 
					            searchParams: url.searchParams,
 | 
				
			||||||
 | 
					            qs: Object.fromEntries(url.searchParams.entries()),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        req.cookies = {};
 | 
				
			||||||
 | 
					        if (req.headers.cookie) {
 | 
				
			||||||
 | 
					            req.headers.cookie.split("; ").forEach(cookie => {
 | 
				
			||||||
 | 
					                const [key, value] = cookie.split("=");
 | 
				
			||||||
 | 
					                req.cookies[key] = decodeURIComponent(value);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return req;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    readBody(req) {
 | 
				
			||||||
 | 
					        return __awaiter(this, void 0, void 0, function* () {
 | 
				
			||||||
 | 
					            return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					                let body = "";
 | 
				
			||||||
 | 
					                req.on("data", (chunk) => {
 | 
				
			||||||
 | 
					                    body += chunk;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                req.on("end", () => {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        resolve(req.headers["content-type"] === "application/json"
 | 
				
			||||||
 | 
					                            ? JSON.parse(body)
 | 
				
			||||||
 | 
					                            : querystring.parse(body));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch (err) {
 | 
				
			||||||
 | 
					                        reject(err);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                req.on("error", reject);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    createResponse(response) {
 | 
				
			||||||
 | 
					        const res = response;
 | 
				
			||||||
 | 
					        res.reply = ({ code = 200, type = "text/html", body }) => {
 | 
				
			||||||
 | 
					            res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` });
 | 
				
			||||||
 | 
					            res.end(body);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        res.json = (body, code = 200) => {
 | 
				
			||||||
 | 
					            res.reply({ code, type: "application/json", body: JSON.stringify(body) });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        res.html = (body, code = 200) => {
 | 
				
			||||||
 | 
					            res.reply({ code, type: "text/html", body });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        res.redirect = (target, code = 302) => {
 | 
				
			||||||
 | 
					            res.writeHead(code, { Location: target });
 | 
				
			||||||
 | 
					            res.end();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										169
									
								
								dist/router.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								dist/router.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
				
			|||||||
 | 
					var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
 | 
				
			||||||
 | 
					    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
 | 
				
			||||||
 | 
					    return new (P || (P = Promise))(function (resolve, reject) {
 | 
				
			||||||
 | 
					        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
 | 
				
			||||||
 | 
					        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
 | 
				
			||||||
 | 
					        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
 | 
				
			||||||
 | 
					        step((generator = generator.apply(thisArg, _arguments || [])).next());
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					import fs from "node:fs";
 | 
				
			||||||
 | 
					import path from "node:path";
 | 
				
			||||||
 | 
					import Tpl from "./template.js";
 | 
				
			||||||
 | 
					export default class Router {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        this.routes = new Map();
 | 
				
			||||||
 | 
					        this.mimes = new Map();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    importRoutesFromPath(p_1) {
 | 
				
			||||||
 | 
					        return __awaiter(this, arguments, void 0, function* (p, tpl = false) {
 | 
				
			||||||
 | 
					            const dirEntries = yield fs.promises.readdir(path.resolve() + '/' + p, { withFileTypes: true });
 | 
				
			||||||
 | 
					            for (const tmp of dirEntries) {
 | 
				
			||||||
 | 
					                if (tmp.isFile() && (tmp.name.endsWith(".mjs") || tmp.name.endsWith(".js"))) {
 | 
				
			||||||
 | 
					                    const routeModule = (yield import(`${path.resolve()}/${p}/${tmp.name}`)).default;
 | 
				
			||||||
 | 
					                    this.use(routeModule(this, tpl));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (tmp.isDirectory()) {
 | 
				
			||||||
 | 
					                    yield this.importRoutesFromPath(p + '/' + tmp.name);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    group(basePath, callback) {
 | 
				
			||||||
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        const methods = {
 | 
				
			||||||
 | 
					            get(path, ...handlers) {
 | 
				
			||||||
 | 
					                const fullPath = self.combinePaths(basePath, path);
 | 
				
			||||||
 | 
					                return self.registerRoute(fullPath, "get", handlers);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            post(path, ...handlers) {
 | 
				
			||||||
 | 
					                const fullPath = self.combinePaths(basePath, path);
 | 
				
			||||||
 | 
					                return self.registerRoute(fullPath, "post", handlers);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            put(path, ...handlers) {
 | 
				
			||||||
 | 
					                const fullPath = self.combinePaths(basePath, path);
 | 
				
			||||||
 | 
					                return self.registerRoute(fullPath, "put", handlers);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            delete(path, ...handlers) {
 | 
				
			||||||
 | 
					                const fullPath = self.combinePaths(basePath, path);
 | 
				
			||||||
 | 
					                return self.registerRoute(fullPath, "delete", handlers);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            patch(path, ...handlers) {
 | 
				
			||||||
 | 
					                const fullPath = self.combinePaths(basePath, path);
 | 
				
			||||||
 | 
					                return self.registerRoute(fullPath, "patch", handlers);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        callback(methods);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    combinePaths(basePath, subPath) {
 | 
				
			||||||
 | 
					        if (typeof basePath === "string" && typeof subPath === "string")
 | 
				
			||||||
 | 
					            return `${basePath.replace(/\/$/, "")}/${subPath.replace(/^\//, "")}`;
 | 
				
			||||||
 | 
					        if (basePath instanceof RegExp && typeof subPath === "string")
 | 
				
			||||||
 | 
					            return new RegExp(`${basePath.source}${subPath.replace(/^\//, "")}`);
 | 
				
			||||||
 | 
					        if (typeof basePath === "string" && subPath instanceof RegExp)
 | 
				
			||||||
 | 
					            return new RegExp(`${basePath.replace(/\/$/, "")}${subPath.source}`);
 | 
				
			||||||
 | 
					        if (basePath instanceof RegExp && subPath instanceof RegExp)
 | 
				
			||||||
 | 
					            return new RegExp(`${basePath.source}${subPath.source}`);
 | 
				
			||||||
 | 
					        throw new TypeError("Invalid path types. Both basePath and subPath must be either string or RegExp.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    use(obj) {
 | 
				
			||||||
 | 
					        if (obj instanceof Router) {
 | 
				
			||||||
 | 
					            this.routes = new Map([...this.routes, ...obj.routes]);
 | 
				
			||||||
 | 
					            this.sortRoutes();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (obj instanceof Tpl) {
 | 
				
			||||||
 | 
					            this.tpl = obj;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    get(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "get", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    post(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "post", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    head(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "head", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    put(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "put", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    delete(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "delete", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    patch(path, cb) {
 | 
				
			||||||
 | 
					        this.registerRoute(path, "patch", cb);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    registerRoute(path, method, handlers) {
 | 
				
			||||||
 | 
					        if (!this.routes.has(path)) {
 | 
				
			||||||
 | 
					            this.routes.set(path, {});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.routes.get(path)[method] = handlers.flat();
 | 
				
			||||||
 | 
					        console.log("route set:", method.toUpperCase(), path);
 | 
				
			||||||
 | 
					        this.sortRoutes();
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getRoute(path, method) {
 | 
				
			||||||
 | 
					        method = method.toLowerCase();
 | 
				
			||||||
 | 
					        return [...this.routes.entries()].find(r => {
 | 
				
			||||||
 | 
					            var _a, _b;
 | 
				
			||||||
 | 
					            return (typeof r[0] === "string" ? r[0] === path : (_b = (_a = r[0]).exec) === null || _b === void 0 ? void 0 : _b.call(_a, path)) && r[1][method];
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    sortRoutes() {
 | 
				
			||||||
 | 
					        this.routes = new Map([...this.routes.entries()].sort().reverse());
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    readMimes(file = "/etc/mime.types") {
 | 
				
			||||||
 | 
					        fs.readFileSync(file, "utf-8")
 | 
				
			||||||
 | 
					            .split("\n")
 | 
				
			||||||
 | 
					            .filter(line => !line.startsWith("#") && line)
 | 
				
			||||||
 | 
					            .forEach(line => {
 | 
				
			||||||
 | 
					            const [mimeType, extensions] = line.split(/\s+/);
 | 
				
			||||||
 | 
					            extensions === null || extensions === void 0 ? void 0 : extensions.split(" ").forEach(ext => this.mimes.set(ext, mimeType));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    static({ dir = path.resolve() + "/public", route = /^\/public/ }) {
 | 
				
			||||||
 | 
					        if (!this.mimes.size)
 | 
				
			||||||
 | 
					            this.readMimes();
 | 
				
			||||||
 | 
					        this.get(route, [(req, res) => {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    const filename = req.url.pathname.replace(route, "") || "index.html";
 | 
				
			||||||
 | 
					                    const mime = this.mimes.get(filename.split(".").pop() || "");
 | 
				
			||||||
 | 
					                    const file = path.join(dir, filename);
 | 
				
			||||||
 | 
					                    const stat = fs.statSync(file);
 | 
				
			||||||
 | 
					                    if (req.headers.range) {
 | 
				
			||||||
 | 
					                        const [startStr, endStr] = req.headers.range.replace(/bytes=/, "").split("-");
 | 
				
			||||||
 | 
					                        const start = parseInt(startStr, 10);
 | 
				
			||||||
 | 
					                        const end = endStr ? parseInt(endStr, 10) : stat.size - 1;
 | 
				
			||||||
 | 
					                        res.writeHead(206, {
 | 
				
			||||||
 | 
					                            "Content-Range": `bytes ${start}-${end}/${stat.size}`,
 | 
				
			||||||
 | 
					                            "Accept-Ranges": "bytes",
 | 
				
			||||||
 | 
					                            "Content-Length": end - start + 1,
 | 
				
			||||||
 | 
					                            "Content-Type": mime,
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        fs.createReadStream(file, { start, end }).pipe(res);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        res.writeHead(200, {
 | 
				
			||||||
 | 
					                            "Content-Length": stat.size,
 | 
				
			||||||
 | 
					                            "Content-Type": mime,
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        fs.createReadStream(file).pipe(res);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (err) {
 | 
				
			||||||
 | 
					                    console.error(err);
 | 
				
			||||||
 | 
					                    res.reply({ code: 404, body: "404 - File not found" });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }]);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										122
									
								
								dist/template.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								dist/template.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					import fs from "node:fs";
 | 
				
			||||||
 | 
					import path from "node:path";
 | 
				
			||||||
 | 
					export default class Template {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        this.views = "./views";
 | 
				
			||||||
 | 
					        this.globals = {};
 | 
				
			||||||
 | 
					        this.debug = false;
 | 
				
			||||||
 | 
					        this.cache = true;
 | 
				
			||||||
 | 
					        this.templates = new Map();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setDebug(debug) {
 | 
				
			||||||
 | 
					        this.debug = debug;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setViews(views) {
 | 
				
			||||||
 | 
					        this.views = views;
 | 
				
			||||||
 | 
					        this.readdir(views);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setGlobals(globals) {
 | 
				
			||||||
 | 
					        this.globals = globals;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setCache(cache) {
 | 
				
			||||||
 | 
					        this.cache = cache;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    readdir(dir, root = dir) {
 | 
				
			||||||
 | 
					        for (const d of fs.readdirSync(`${path.resolve()}/${dir}`)) {
 | 
				
			||||||
 | 
					            if (d.endsWith(".html")) {
 | 
				
			||||||
 | 
					                const file = path.parse(`${dir.replace(this.views, "")}/${d}`);
 | 
				
			||||||
 | 
					                const t_dir = file.dir.substring(1);
 | 
				
			||||||
 | 
					                const t_file = file.name;
 | 
				
			||||||
 | 
					                this.getTemplate(!t_dir ? t_file : `${t_dir}/${t_file}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                this.readdir(`${dir}/${d}`, root);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getTemplate(tpl) {
 | 
				
			||||||
 | 
					        let template;
 | 
				
			||||||
 | 
					        let cache = false;
 | 
				
			||||||
 | 
					        if (this.cache && this.templates.has(tpl)) {
 | 
				
			||||||
 | 
					            template = this.templates.get(tpl);
 | 
				
			||||||
 | 
					            cache = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            template = {
 | 
				
			||||||
 | 
					                code: fs.readFileSync(`${this.views}/${tpl}.html`, "utf-8"),
 | 
				
			||||||
 | 
					                cached: new Date(),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            this.templates.set(tpl, template);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            template.code,
 | 
				
			||||||
 | 
					            this.debug ? `<!-- ${tpl}.html ${cache ? `cached ${template.cached}` : "not cached"} -->` : "",
 | 
				
			||||||
 | 
					        ].join("");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    render(file, data = {}, locals = {}) {
 | 
				
			||||||
 | 
					        data = Object.assign(Object.assign(Object.assign({}, data), locals), this.globals);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const code = 'with(_data){const __html = [];' +
 | 
				
			||||||
 | 
					                '__html.push(`' +
 | 
				
			||||||
 | 
					                this.getTemplate(file)
 | 
				
			||||||
 | 
					                    .replace(/[\t]/g, " ")
 | 
				
			||||||
 | 
					                    .split("`")
 | 
				
			||||||
 | 
					                    .join("\\`")
 | 
				
			||||||
 | 
					                    .replace(/{{--(.*?)--}}/g, "")
 | 
				
			||||||
 | 
					                    .replace(/{{(.+?)}}/g, "`, $1, `")
 | 
				
			||||||
 | 
					                    .replace(/{!!(.+?)!!}/g, "`, this.escape($1), `")
 | 
				
			||||||
 | 
					                    .replace(/@js/g, "`);")
 | 
				
			||||||
 | 
					                    .replace(/@endjs/g, ";__html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@mtime\((.*?)\)/g, "`);__html.push(this.getMtime('$1'));__html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@include\((.*?)\)/g, (_, inc) => this.render(inc, data))
 | 
				
			||||||
 | 
					                    .replace(/@for\((.*?)\)$/gm, "`); for($1) { __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@endfor/g, "`); } __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@each\((.*?) as (.*?)\)/g, "`); this.forEach($1, ($2, key) => { __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@endeach/g, "`); }); __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@elseif\((.*?)\)(\)?)/g, "`); } else if ($1$2) { __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@if\((.*?)\)(\)?)/g, "`); if ($1$2) { __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@else/g, "`); } else { __html.push(`")
 | 
				
			||||||
 | 
					                    .replace(/@endif/g, "`); } __html.push(`") +
 | 
				
			||||||
 | 
					                "`); return __html.join('').replace(/\\n\\s*\\n/g, '\\n'); }";
 | 
				
			||||||
 | 
					            return new Function("_data", code).bind({
 | 
				
			||||||
 | 
					                escape: this.escape,
 | 
				
			||||||
 | 
					                forEach: this.forEach,
 | 
				
			||||||
 | 
					                getMtime: this.getMtime,
 | 
				
			||||||
 | 
					            })(data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (err) {
 | 
				
			||||||
 | 
					            console.log(file, err.message);
 | 
				
			||||||
 | 
					            return this.debug ? `${err.message} in ${file}` : "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    escape(str) {
 | 
				
			||||||
 | 
					        return (str + "")
 | 
				
			||||||
 | 
					            .replace(/&/g, "&")
 | 
				
			||||||
 | 
					            .replace(/</g, "<")
 | 
				
			||||||
 | 
					            .replace(/>/g, ">")
 | 
				
			||||||
 | 
					            .replace(/"/g, """)
 | 
				
			||||||
 | 
					            .replace(/'/g, "'")
 | 
				
			||||||
 | 
					            .replace(/{/g, "{")
 | 
				
			||||||
 | 
					            .replace(/}/g, "}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    forEach(o, f) {
 | 
				
			||||||
 | 
					        if (Array.isArray(o)) {
 | 
				
			||||||
 | 
					            o.forEach(f);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (typeof o === "object") {
 | 
				
			||||||
 | 
					            Object.keys(o).forEach((k) => f.call(null, o[k], k));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            throw new Error(`${o} is not an iterable object`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getMtime(file) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return +`${fs.statSync(path.normalize(process.cwd() + file)).mtimeMs}`.split(".")[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (err) {
 | 
				
			||||||
 | 
					            console.log(err);
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,10 +2,9 @@
 | 
				
			|||||||
  "name": "flummpress",
 | 
					  "name": "flummpress",
 | 
				
			||||||
  "version": "3.0.0",
 | 
					  "version": "3.0.0",
 | 
				
			||||||
  "description": "Express für arme",
 | 
					  "description": "Express für arme",
 | 
				
			||||||
  "main": "index.mjs",
 | 
					  "main": "./dist/index.js",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "build": "tsc",
 | 
					    "build": "tsc",
 | 
				
			||||||
    "start": "node ./dist/index.js",
 | 
					 | 
				
			||||||
    "doc": "typedoc --out docs src"
 | 
					    "doc": "typedoc --out docs src"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [],
 | 
					  "keywords": [],
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								src/test.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/test.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import flummpress from '../dist/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					process.on('unhandledRejection', err => {
 | 
				
			||||||
 | 
					  console.error(err);
 | 
				
			||||||
 | 
					  throw err;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const app = new flummpress();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const loggedin = async (req, res, next) => {
 | 
				
			||||||
 | 
					  console.log("Logged in");
 | 
				
			||||||
 | 
					  await next();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.router.group(/^\/api\/v2\/admin\/(?<postid>\d+)\/tags/, group => {
 | 
				
			||||||
 | 
					  group.get(/$/,
 | 
				
			||||||
 | 
					    (req, res, next) => { // middleware davor
 | 
				
			||||||
 | 
					      console.log("Logged in");
 | 
				
			||||||
 | 
					      next();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    (req, res, next) => { // eigentlicher request
 | 
				
			||||||
 | 
					      res.reply({
 | 
				
			||||||
 | 
					        body: JSON.stringify(req.params)
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      next();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    (req, res) => { // middleware danach
 | 
				
			||||||
 | 
					      console.log("Logged out");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.listen(3000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(app.router);
 | 
				
			||||||
		Reference in New Issue
	
	Block a user