refactored...
This commit is contained in:
		
							
								
								
									
										82
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								README.md
									
									
									
									
									
								
							@@ -1,42 +1,42 @@
 | 
				
			|||||||
# flummpress
 | 
					# flummpress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Usage Example
 | 
					## Usage Example
 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
import path from "path";
 | 
					import path from "path";
 | 
				
			||||||
import flummpress, { router, views } from "flummpress";
 | 
					import flummpress, { router, views } from "flummpress";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(async () => {
 | 
					(async () => {
 | 
				
			||||||
  const port = 8080;
 | 
					  const port = 8080;
 | 
				
			||||||
  (await new flummpress())
 | 
					  (await new flummpress())
 | 
				
			||||||
    .listen(port)
 | 
					    .listen(port)
 | 
				
			||||||
    .on("listening", () => {
 | 
					    .on("listening", () => {
 | 
				
			||||||
      console.log(`flummpress is listening on port ${port}`);
 | 
					      console.log(`flummpress is listening on port ${port}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // new route GET
 | 
					      // new route GET
 | 
				
			||||||
      router.get(/^\/$/, (req, res) => {
 | 
					      router.get(/^\/$/, (req, res) => {
 | 
				
			||||||
        res.reply({
 | 
					        res.reply({
 | 
				
			||||||
          body: "hello world!"
 | 
					          body: "hello world!"
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // new route POST
 | 
					      // new route POST
 | 
				
			||||||
      router.post(/^\/$/, async (req, res) => {
 | 
					      router.post(/^\/$/, async (req, res) => {
 | 
				
			||||||
        const postdata = await req.post;
 | 
					        const postdata = await req.post;
 | 
				
			||||||
        console.log(postdata);
 | 
					        console.log(postdata);
 | 
				
			||||||
        res.reply({
 | 
					        res.reply({
 | 
				
			||||||
          body: "hello post!"
 | 
					          body: "hello post!"
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // public folder
 | 
					      // public folder
 | 
				
			||||||
      router.static({
 | 
					      router.static({
 | 
				
			||||||
        dir: path.resolve() + "/public",
 | 
					        dir: path.resolve() + "/public",
 | 
				
			||||||
        route: /^\/public/
 | 
					        route: /^\/public/
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## documentation
 | 
					## documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
coming soon
 | 
					coming soon
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "flummpress",
 | 
					  "name": "flummpress",
 | 
				
			||||||
  "version": "1.0.2",
 | 
					  "version": "2.0.0",
 | 
				
			||||||
  "description": "Express für arme",
 | 
					  "description": "Express für arme",
 | 
				
			||||||
  "main": "src/index.mjs",
 | 
					  "main": "src/index.mjs",
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										224
									
								
								src/index.mjs
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								src/index.mjs
									
									
									
									
									
								
							@@ -1,46 +1,178 @@
 | 
				
			|||||||
import http from "http";
 | 
					import path from "path";
 | 
				
			||||||
import url from "url";
 | 
					import fs from "fs";
 | 
				
			||||||
import querystring from "querystring";
 | 
					import http from "http";
 | 
				
			||||||
 | 
					import { URL } from "url";
 | 
				
			||||||
import router from "./router.mjs";
 | 
					import querystring from "querystring";
 | 
				
			||||||
import views from "./views.mjs";
 | 
					
 | 
				
			||||||
 | 
					import Router from "./router.mjs";
 | 
				
			||||||
export default class flummpress {
 | 
					import Tpl from "./template.mjs";
 | 
				
			||||||
  constructor() {
 | 
					
 | 
				
			||||||
    this.router = new router();
 | 
					export { Router, Tpl };
 | 
				
			||||||
    this.views = new views();
 | 
					
 | 
				
			||||||
  }
 | 
					export default class flummpress {
 | 
				
			||||||
 | 
					  #mimes;
 | 
				
			||||||
  use(item) {
 | 
					  #server;
 | 
				
			||||||
    switch(item.type) {
 | 
					  constructor() {
 | 
				
			||||||
      case "route":
 | 
					    this.router = new Router();
 | 
				
			||||||
        item.data.forEach((v, k) => this.router.routes.set(k, v));
 | 
					    this.tpl = new Tpl();
 | 
				
			||||||
      break;
 | 
					
 | 
				
			||||||
      case "view":
 | 
					    return this;
 | 
				
			||||||
        item.data.forEach((v, k) => this.views.set(k, v));
 | 
					  };
 | 
				
			||||||
      break;
 | 
					
 | 
				
			||||||
    }
 | 
					  use(obj) {
 | 
				
			||||||
  }
 | 
					    if(obj instanceof Router) {
 | 
				
			||||||
 | 
					      this.router.routes = new Map([ ...this.router.routes, ...obj.routes ]);
 | 
				
			||||||
  listen(...args) {
 | 
					      this.router.sortRoutes();
 | 
				
			||||||
    this.router.routes.forEach((v, k) => console.log("route set", v.method, k));
 | 
					    }
 | 
				
			||||||
    return http.createServer(async (req, res, r) => {
 | 
					    else if(obj instanceof Tpl) {
 | 
				
			||||||
      req.url = url.parse(req.url.replace(/(?!^.)(\/+)?%/, ''));
 | 
					      this.tpl = obj;
 | 
				
			||||||
      req.url.qs = querystring.parse(req.url.query);
 | 
					    }
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
      req.post = new Promise((resolve, _, data = "") => req
 | 
					  };
 | 
				
			||||||
        .on("data", d => void (data += d))
 | 
					
 | 
				
			||||||
        .on("end", () => void resolve(Object.fromEntries(Object.entries(querystring.parse(data)).map(([k, v]) => [k, decodeURIComponent(v)])))));
 | 
					  static({ dir = path.resolve() + "/public", route = /^\/public/ }) {
 | 
				
			||||||
      
 | 
					    if(!this.#mimes) {
 | 
				
			||||||
      res.reply = ({
 | 
					      this.#mimes = new Map();
 | 
				
			||||||
        code = 200,
 | 
					      (fs.readFileSync("./mime.types", "utf-8"))
 | 
				
			||||||
        type = "text/html",
 | 
					        .split("\n")
 | 
				
			||||||
        body
 | 
					        .filter(e => !e.startsWith("#") && e)
 | 
				
			||||||
      }) => res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` }).end(body);
 | 
					        .map(e => e.split(/\s{2,}/))
 | 
				
			||||||
      
 | 
					        .filter(e => e.length > 1)
 | 
				
			||||||
      !(r = this.router.routes.getRegex(req.url.pathname, req.method)) ? res.writeHead(404).end(`404 - ${req.url.pathname}`) : await r(req, res);
 | 
					        .forEach(m => m[1].split(" ").forEach(ext => this.#mimes.set(ext, m[0])));
 | 
				
			||||||
      console.log(`[${(new Date()).toLocaleTimeString()}] ${res.statusCode} ${req.method}\t${req.url.pathname}`);
 | 
					    }
 | 
				
			||||||
    }).listen(...args);
 | 
					
 | 
				
			||||||
  }
 | 
					    this.router.get(route, (req, res) => {
 | 
				
			||||||
};
 | 
					      try {
 | 
				
			||||||
export { router, views };
 | 
					        const filename = req.url.pathname.replace(route, "") || "index.html";
 | 
				
			||||||
 | 
					        const mime = this.#mimes.get(filename.split(".").pop());
 | 
				
			||||||
 | 
					        const file = path.join(dir, filename);
 | 
				
			||||||
 | 
					        let stat;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          stat = fs.statSync(file);
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					          res.reply({
 | 
				
			||||||
 | 
					            code: 404,
 | 
				
			||||||
 | 
					            body: "404 - file not found."
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!mime.startsWith("video") && !mime.startsWith("audio")) {
 | 
				
			||||||
 | 
					          res.reply({
 | 
				
			||||||
 | 
					            type: this.#mimes.get(filename.split(".").pop()).toLowerCase(),
 | 
				
			||||||
 | 
					            body: fs.readFileSync(path.join(dir, filename))
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(req.headers.range) {
 | 
				
			||||||
 | 
					          const parts = req.headers.range.replace(/bytes=/, "").split("-");
 | 
				
			||||||
 | 
					          const start = parseInt(parts[0], 10);
 | 
				
			||||||
 | 
					          const end = parts[1] ? parseInt(parts[1], 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,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          const stream = fs.createReadStream(file, { start: start, end: end })
 | 
				
			||||||
 | 
					            .on("open", () => stream.pipe(res))
 | 
				
			||||||
 | 
					            .on("error", err => res.end(err));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					          res.writeHead(200, {
 | 
				
			||||||
 | 
					            "Content-Length": stat.size,
 | 
				
			||||||
 | 
					            "Content-Type": mime,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          fs.createReadStream(file).pipe(res);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch(err) {
 | 
				
			||||||
 | 
					        console.error(err);
 | 
				
			||||||
 | 
					        res.reply({
 | 
				
			||||||
 | 
					          code: 500,
 | 
				
			||||||
 | 
					          body: "500 - f0ck hat keinen b0ck"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  listen(...args) {
 | 
				
			||||||
 | 
					    this.#server = http.createServer(async (req, res) => {
 | 
				
			||||||
 | 
					      const t_start = process.hrtime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const _url = new URL(req.url.replace(/(?!^.)(\/+)?$/, ''), "relative:///");
 | 
				
			||||||
 | 
					      req.url = {
 | 
				
			||||||
 | 
					        pathname: _url.pathname,
 | 
				
			||||||
 | 
					        split: _url.pathname.split("/").slice(1),
 | 
				
			||||||
 | 
					        searchParams: _url.searchParams,
 | 
				
			||||||
 | 
					        qs: {...querystring.parse(_url.search.substring(1))} // legacy
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const method = req.method.toLowerCase();
 | 
				
			||||||
 | 
					      const route = this.router.getRoute(req.url.pathname, req.method);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if(route) { // 200
 | 
				
			||||||
 | 
					        const cb = route[1][method];
 | 
				
			||||||
 | 
					        const middleware = route[1][`${method}mw`];
 | 
				
			||||||
 | 
					        req.params = req.url.pathname.match(new RegExp(route[0]))?.groups;
 | 
				
			||||||
 | 
					        req.post = await this.readBody(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const result = await this.processMiddleware(middleware, req, this.createResponse(res));
 | 
				
			||||||
 | 
					        if(result)
 | 
				
			||||||
 | 
					          cb(req, res);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else { // 404
 | 
				
			||||||
 | 
					        res
 | 
				
			||||||
 | 
					          .writeHead(404)
 | 
				
			||||||
 | 
					          .end(`404 - ${req.method} ${req.url.pathname}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log([
 | 
				
			||||||
 | 
					        `[${(new Date()).toLocaleTimeString()}]`,
 | 
				
			||||||
 | 
					        `${(process.hrtime(t_start)[1] / 1e6).toFixed(2)}ms`,
 | 
				
			||||||
 | 
					        `${req.method} ${res.statusCode}`,
 | 
				
			||||||
 | 
					        req.url.pathname
 | 
				
			||||||
 | 
					      ].map(e => e.toString().padEnd(15)).join(""));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }).listen(...args);
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  readBody(req) {
 | 
				
			||||||
 | 
					    return new Promise((resolve, _, data = "") => req
 | 
				
			||||||
 | 
					      .on("data", d => void (data += d))
 | 
				
			||||||
 | 
					      .on("end", () => {
 | 
				
			||||||
 | 
					        if(req.headers['content-type'] === "application/json") {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            return void resolve(JSON.parse(data));
 | 
				
			||||||
 | 
					          } catch(err) {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        void resolve(Object.fromEntries(Object.entries(querystring.parse(data)).map(([k, v]) => {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            return [k, decodeURIComponent(v)];
 | 
				
			||||||
 | 
					          } catch(err) {
 | 
				
			||||||
 | 
					            return [k, v];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })));
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createResponse(res) {
 | 
				
			||||||
 | 
					    res.reply = ({
 | 
				
			||||||
 | 
					      code = 200,
 | 
				
			||||||
 | 
					      type = "text/html",
 | 
				
			||||||
 | 
					      body
 | 
				
			||||||
 | 
					    }) => res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` }).end(body);
 | 
				
			||||||
 | 
					    res.json = msg => res.reply({ type: "application/json", body: msg });
 | 
				
			||||||
 | 
					    res.html = msg => res.reply({ type: "text/html", body: msg });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  processMiddleware(middleware, req, res) {
 | 
				
			||||||
 | 
					    if(!middleware)
 | 
				
			||||||
 | 
					      return new Promise(resolve => resolve(true));
 | 
				
			||||||
 | 
					    return new Promise(resolve => middleware(req, res, () => resolve(true)));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										126
									
								
								src/router.mjs
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/router.mjs
									
									
									
									
									
								
							@@ -1,58 +1,68 @@
 | 
				
			|||||||
import { promises as fs } from "fs";
 | 
					export default class Router {
 | 
				
			||||||
import path from "path";
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.routes = new Map();
 | 
				
			||||||
export default class Router {
 | 
					    return this;
 | 
				
			||||||
  #mimes;
 | 
					  };
 | 
				
			||||||
  constructor() {
 | 
					
 | 
				
			||||||
    this.routes = new Map();
 | 
					  group(path, cb) {
 | 
				
			||||||
  }
 | 
					    const methods = {
 | 
				
			||||||
  async loadRoutes(path) {
 | 
					      get:  this.get.bind(this),
 | 
				
			||||||
    await Promise.all(
 | 
					      post: this.post.bind(this),
 | 
				
			||||||
      (await fs.readdir(path))
 | 
					    };
 | 
				
			||||||
        .filter(f => f.endsWith(".mjs"))
 | 
					    const target = {
 | 
				
			||||||
        .map(async route => (await import(`${path}/${route}`)).default(this))
 | 
					      path: new RegExp(path),
 | 
				
			||||||
    );
 | 
					    };
 | 
				
			||||||
  }
 | 
					    const handler = {
 | 
				
			||||||
  route(method, args) {
 | 
					      get: (opt, method) => (p, ...args) => methods[method](
 | 
				
			||||||
    //console.log("route set", method, args[0]);
 | 
					        new RegExp([ opt.path, new RegExp(p === "/" ? "$": p) ]
 | 
				
			||||||
    return {
 | 
					          .map(regex => regex.source)
 | 
				
			||||||
      type: "route",
 | 
					          .join("")
 | 
				
			||||||
      data: this.routes.set(args[0], { method: method, f: args[1] })
 | 
					          .replace(/(\\\/){1,}/g, "/")),
 | 
				
			||||||
    };
 | 
					        ...args,
 | 
				
			||||||
  }
 | 
					      )
 | 
				
			||||||
  get() {
 | 
					    };
 | 
				
			||||||
    return this.route("GET", arguments);
 | 
					    cb(new Proxy(target, handler));
 | 
				
			||||||
  }
 | 
					    return this;
 | 
				
			||||||
  post() {
 | 
					  };
 | 
				
			||||||
    return this.route("POST", arguments);
 | 
					
 | 
				
			||||||
  }
 | 
					  get(path, ...args) {
 | 
				
			||||||
  async static({ dir = path.resolve() + "/public", route = /^\/public/ }) {
 | 
					    if(args.length === 1)
 | 
				
			||||||
    if(!this.#mimes) {
 | 
					      this.registerRoute(path, args[0], "get");
 | 
				
			||||||
      this.#mimes = new Map();
 | 
					    else
 | 
				
			||||||
      (await fs.readFile("/etc/mime.types", "utf-8"))
 | 
					      this.registerRoute(path, args[1], "get", args[0]);
 | 
				
			||||||
        .split("\n")
 | 
					    return this;
 | 
				
			||||||
        .filter(e => !e.startsWith("#") && e)
 | 
					  };
 | 
				
			||||||
        .map(e => e.split(/\s{2,}/))
 | 
					
 | 
				
			||||||
        .filter(e => e.length > 1)
 | 
					  post(path, ...args) {
 | 
				
			||||||
        .forEach(m => m[1].split(" ").forEach(ext => this.#mimes.set(ext, m[0])));
 | 
					    if(args.length === 1)
 | 
				
			||||||
    }
 | 
					      this.registerRoute(path, args[0], "post");
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
    this.get(route, async (req, res) => {
 | 
					      this.registerRoute(path, args[1], "post", args[0]);
 | 
				
			||||||
      try {
 | 
					    return this;
 | 
				
			||||||
        return res.reply({
 | 
					  };
 | 
				
			||||||
          type: this.#mimes.get(req.url.path.split(".").pop()).toLowerCase(),
 | 
					
 | 
				
			||||||
          body: await fs.readFile(path.join(dir, req.url.path.replace(route, "")), "utf-8")
 | 
					  registerRoute(path, cb, method, middleware) {
 | 
				
			||||||
        });
 | 
					    if(!this.routes.has(path))
 | 
				
			||||||
      } catch {
 | 
					      this.routes.set(path, {});
 | 
				
			||||||
        return res.reply({
 | 
					    this.routes.set(path, {
 | 
				
			||||||
          code: 404,
 | 
					      ...this.routes.get(path),
 | 
				
			||||||
          body: "404 - file not found"
 | 
					      [method]: cb,
 | 
				
			||||||
        });
 | 
					      [method + "mw"]: middleware,
 | 
				
			||||||
      }
 | 
					    });
 | 
				
			||||||
    });
 | 
					    console.log("route set:", method.toUpperCase(), path);
 | 
				
			||||||
  }
 | 
					    this.sortRoutes();
 | 
				
			||||||
};
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
Map.prototype.getRegex = function(path, method) {
 | 
					  
 | 
				
			||||||
  return [...this.entries()].filter(r => r[0].exec(path) && r[1].method.includes(method))[0]?.[1].f;
 | 
					  getRoute(path, method) {
 | 
				
			||||||
};
 | 
					    method = method.toLowerCase();
 | 
				
			||||||
 | 
					    return [...this.routes.entries()].filter(r => {
 | 
				
			||||||
 | 
					      return (r[0] === path || r[0].exec?.(path)) && r[1].hasOwnProperty(method);
 | 
				
			||||||
 | 
					    })[0];
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sortRoutes() {
 | 
				
			||||||
 | 
					    this.routes = new Map([...this.routes.entries()].sort().reverse());
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								src/template.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/template.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					import fs from "fs";
 | 
				
			||||||
 | 
					import path from "path";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class {
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.views = path.resolve() + "/views";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  getTemplate(tpl) {
 | 
				
			||||||
 | 
					    return fs.readFileSync(`${this.views}/${tpl}.html`, "utf-8");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  render(tpl, data = {}) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const code = 'with(_data){const __html = [];' +
 | 
				
			||||||
 | 
					        '__html.push(\`' +
 | 
				
			||||||
 | 
					        tpl.replace(/[\t]/g, ' ')
 | 
				
			||||||
 | 
					          .replace(/'(?=[^\{]*}})/, '\t')
 | 
				
			||||||
 | 
					          .split('\`').join('\\\`')
 | 
				
			||||||
 | 
					          .split('\t').join('\`')
 | 
				
			||||||
 | 
					          .replace(/{{=(.+?)}}/g, '\`,$1,\`')
 | 
				
			||||||
 | 
					          .replace(/{{-(.+?)}}/g, '\`,this.escape($1),\`')
 | 
				
			||||||
 | 
					          .replace(/{{include (.*?)}}/g, (_, inc) => this.render(this.getTemplate(inc), data))
 | 
				
			||||||
 | 
					          .replace(/{{each (?<key>.*) as (?<val>.*)}}/g, (_, key, val) => `\`);this.forEach(${key},(${val},key)=>{__html.push(\``)
 | 
				
			||||||
 | 
					          .replace(/{{\/each}}/g, "\`);});__html.push(`")
 | 
				
			||||||
 | 
					          .split('{{').join('\`);')
 | 
				
			||||||
 | 
					          .split('}}').join('__html.push(\`')
 | 
				
			||||||
 | 
					        + '\`);return __html.join(\'\').replace(/\\n\\s*\\n/g, "\\n");}';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return (new Function("_data", code)).bind({
 | 
				
			||||||
 | 
					        escape: this.escape,
 | 
				
			||||||
 | 
					        forEach: this.forEach
 | 
				
			||||||
 | 
					      })(data);
 | 
				
			||||||
 | 
					    } catch(err) {
 | 
				
			||||||
 | 
					      console.log(err);
 | 
				
			||||||
 | 
					      return err.message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  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 a iterable object`);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
import { promises as fs } from "fs";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class ViewController {
 | 
					 | 
				
			||||||
  #views;
 | 
					 | 
				
			||||||
  constructor() {
 | 
					 | 
				
			||||||
    this.#views = new Map();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  get(name) {
 | 
					 | 
				
			||||||
    return this.#views.get(name) || false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  set(name, view) {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      type: "view",
 | 
					 | 
				
			||||||
      data: this.#views.set(name, view)
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  async loadViews(path) {
 | 
					 | 
				
			||||||
    return await Promise.all((await fs.readdir(path))
 | 
					 | 
				
			||||||
      .filter(view => view.endsWith(".html"))
 | 
					 | 
				
			||||||
      .map(async view => this.set(view.slice(0, -5), await fs.readFile(`${path}/${view}`))));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user