blah
This commit is contained in:
		
							
								
								
									
										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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user