...
This commit is contained in:
		@@ -1,5 +1,3 @@
 | 
				
			|||||||
import path from "path";
 | 
					 | 
				
			||||||
import fs from "fs";
 | 
					 | 
				
			||||||
import http from "http";
 | 
					import http from "http";
 | 
				
			||||||
import { URL } from "url";
 | 
					import { URL } from "url";
 | 
				
			||||||
import querystring from "querystring";
 | 
					import querystring from "querystring";
 | 
				
			||||||
@@ -10,90 +8,26 @@ import Tpl from "./template.mjs";
 | 
				
			|||||||
export { Router, Tpl };
 | 
					export { Router, Tpl };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class flummpress {
 | 
					export default class flummpress {
 | 
				
			||||||
  #mimes;
 | 
					 | 
				
			||||||
  #server;
 | 
					  #server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.router = new Router();
 | 
					    this.router = new Router();
 | 
				
			||||||
    this.tpl = new Tpl();
 | 
					    this.tpl = new Tpl();
 | 
				
			||||||
 | 
					    this.middleware = new Set();
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  use(obj) {
 | 
					  use(obj) {
 | 
				
			||||||
    if(obj instanceof Router) {
 | 
					    if(obj instanceof Router) {
 | 
				
			||||||
      this.router.routes = new Map([ ...this.router.routes, ...obj.routes ]);
 | 
					      this.router.use(obj);
 | 
				
			||||||
      this.router.sortRoutes();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if(obj instanceof Tpl) {
 | 
					    else if(obj instanceof Tpl) {
 | 
				
			||||||
      this.tpl = obj;
 | 
					      this.tpl = obj;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return this;
 | 
					    else {
 | 
				
			||||||
  };
 | 
					      if(!this.middleware.has(obj))
 | 
				
			||||||
 | 
					        this.middleware.add(obj);
 | 
				
			||||||
  static({ dir = path.resolve() + "/public", route = /^\/public/ }) {
 | 
					 | 
				
			||||||
    if(!this.#mimes) {
 | 
					 | 
				
			||||||
      this.#mimes = new Map();
 | 
					 | 
				
			||||||
      (fs.readFileSync("./mime.types", "utf-8"))
 | 
					 | 
				
			||||||
        .split("\n")
 | 
					 | 
				
			||||||
        .filter(e => !e.startsWith("#") && e)
 | 
					 | 
				
			||||||
        .map(e => e.split(/\s{2,}/))
 | 
					 | 
				
			||||||
        .filter(e => e.length > 1)
 | 
					 | 
				
			||||||
        .forEach(m => m[1].split(" ").forEach(ext => this.#mimes.set(ext, m[0])));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.router.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);
 | 
					 | 
				
			||||||
        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;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,6 +43,16 @@ export default class flummpress {
 | 
				
			|||||||
        qs: {...querystring.parse(_url.search.substring(1))} // legacy
 | 
					        qs: {...querystring.parse(_url.search.substring(1))} // legacy
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      req.cookies = {};
 | 
				
			||||||
 | 
					      if(req.headers.cookie) {
 | 
				
			||||||
 | 
					        req.headers.cookie.split("; ").forEach(c => {
 | 
				
			||||||
 | 
					          const parts = c.split('=');
 | 
				
			||||||
 | 
					          req.cookies[parts.shift().trim()] = decodeURI(parts.join('='));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await Promise.all([...this.middleware].map(m => m(req, res)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const method = req.method.toLowerCase();
 | 
					      const method = req.method.toLowerCase();
 | 
				
			||||||
      const route = this.router.getRoute(req.url.pathname, req.method);
 | 
					      const route = this.router.getRoute(req.url.pathname, req.method);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -166,6 +110,10 @@ export default class flummpress {
 | 
				
			|||||||
    }) => res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` }).end(body);
 | 
					    }) => res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` }).end(body);
 | 
				
			||||||
    res.json = msg => res.reply({ type: "application/json", body: msg });
 | 
					    res.json = msg => res.reply({ type: "application/json", body: msg });
 | 
				
			||||||
    res.html = msg => res.reply({ type: "text/html", body: msg });
 | 
					    res.html = msg => res.reply({ type: "text/html", body: msg });
 | 
				
			||||||
 | 
					    res.redirect = (target, code = 307) => res.writeHead(code, {
 | 
				
			||||||
 | 
					      "Cache-Control": "no-cache, public",
 | 
				
			||||||
 | 
					      "Location": target
 | 
				
			||||||
 | 
					    }).end();
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										101
									
								
								src/router.mjs
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/router.mjs
									
									
									
									
									
								
							@@ -1,9 +1,27 @@
 | 
				
			|||||||
 | 
					import fs from "fs";
 | 
				
			||||||
 | 
					import path from "path";
 | 
				
			||||||
 | 
					import Tpl from "./template.mjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Router {
 | 
					export default class Router {
 | 
				
			||||||
 | 
					  #mimes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.routes = new Map();
 | 
					    this.routes = new Map();
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async importRoutesFromPath(p, tpl = false) {
 | 
				
			||||||
 | 
					    await Promise.all(
 | 
				
			||||||
 | 
					      fs.readdirSync(path.resolve() + "/" + p)
 | 
				
			||||||
 | 
					        .filter(r => r.endsWith(".mjs"))
 | 
				
			||||||
 | 
					        .map(async r => {
 | 
				
			||||||
 | 
					          const tmp = (await import(`${path.resolve()}/${p}/${r}`)).default(this, tpl);
 | 
				
			||||||
 | 
					          this.use(tmp);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  group(path, cb) {
 | 
					  group(path, cb) {
 | 
				
			||||||
    const methods = {
 | 
					    const methods = {
 | 
				
			||||||
      get:  this.get.bind(this),
 | 
					      get:  this.get.bind(this),
 | 
				
			||||||
@@ -25,6 +43,16 @@ export default class Router {
 | 
				
			|||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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, ...args) {
 | 
					  get(path, ...args) {
 | 
				
			||||||
    if(args.length === 1)
 | 
					    if(args.length === 1)
 | 
				
			||||||
      this.registerRoute(path, args[0], "get");
 | 
					      this.registerRoute(path, args[0], "get");
 | 
				
			||||||
@@ -53,7 +81,7 @@ export default class Router {
 | 
				
			|||||||
    this.sortRoutes();
 | 
					    this.sortRoutes();
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
  getRoute(path, method) {
 | 
					  getRoute(path, method) {
 | 
				
			||||||
    method = method.toLowerCase();
 | 
					    method = method.toLowerCase();
 | 
				
			||||||
    return [...this.routes.entries()].filter(r => {
 | 
					    return [...this.routes.entries()].filter(r => {
 | 
				
			||||||
@@ -65,4 +93,75 @@ export default class Router {
 | 
				
			|||||||
    this.routes = new Map([...this.routes.entries()].sort().reverse());
 | 
					    this.routes = new Map([...this.routes.entries()].sort().reverse());
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  readMimes(file = "/etc/mime.types") {
 | 
				
			||||||
 | 
					    this.#mimes = new Map();
 | 
				
			||||||
 | 
					    (fs.readFileSync(file, "utf-8"))
 | 
				
			||||||
 | 
					      .split("\n")
 | 
				
			||||||
 | 
					      .filter(e => !e.startsWith("#") && e)
 | 
				
			||||||
 | 
					      .map(e => e.split(/\s{2,}/))
 | 
				
			||||||
 | 
					      .filter(e => e.length > 1)
 | 
				
			||||||
 | 
					      .forEach(m => m[1].split(" ").forEach(ext => this.#mimes.set(ext, m[0])));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static({ dir = path.resolve() + "/public", route = /^\/public/ }) {
 | 
				
			||||||
 | 
					    if(!this.#mimes)
 | 
				
			||||||
 | 
					      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);
 | 
				
			||||||
 | 
					        let stat;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          stat = fs.statSync(file);
 | 
				
			||||||
 | 
					        } catch(err) {
 | 
				
			||||||
 | 
					          console.log(err);
 | 
				
			||||||
 | 
					          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 - internal server error"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										105
									
								
								src/template.mjs
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								src/template.mjs
									
									
									
									
									
								
							@@ -2,38 +2,108 @@ import fs from "fs";
 | 
				
			|||||||
import path from "path";
 | 
					import path from "path";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class {
 | 
					export default class {
 | 
				
			||||||
 | 
					  #views;
 | 
				
			||||||
 | 
					  #globals;
 | 
				
			||||||
 | 
					  #cache;
 | 
				
			||||||
 | 
					  #templates;
 | 
				
			||||||
 | 
					  #debug;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.views = path.resolve() + "/views";
 | 
					    this.#views = "./views";
 | 
				
			||||||
 | 
					    this.#globals = {};
 | 
				
			||||||
 | 
					    this.#debug = false;
 | 
				
			||||||
 | 
					    this.#cache = true;
 | 
				
			||||||
 | 
					    this.#templates = new Map();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  getTemplate(tpl) {
 | 
					
 | 
				
			||||||
    return fs.readFileSync(`${this.views}/${tpl}.html`, "utf-8");
 | 
					  set debug(d) {
 | 
				
			||||||
 | 
					    this.#debug = !!d;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  set views(v) {
 | 
				
			||||||
 | 
					    this.#views = v;
 | 
				
			||||||
 | 
					    this.readdir(v);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  set globals(g) {
 | 
				
			||||||
 | 
					    this.#globals = g;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  set cache(c) {
 | 
				
			||||||
 | 
					    this.#cache = !!c;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  readdir(dir, root = dir) {
 | 
				
			||||||
 | 
					    for(const d of fs.readdirSync(`${path.resolve()}/${dir}`)) {
 | 
				
			||||||
 | 
					      if(d.endsWith(".html")) { // template
 | 
				
			||||||
 | 
					        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 { // directory
 | 
				
			||||||
 | 
					        this.readdir(`${dir}/${d}`, root);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  render(tpl, data = {}) {
 | 
					
 | 
				
			||||||
 | 
					  getTemplate(tpl) {
 | 
				
			||||||
 | 
					    let template = {};
 | 
				
			||||||
 | 
					    let cache = false;
 | 
				
			||||||
 | 
					    if(this.#cache && this.#templates.has(tpl)) {
 | 
				
			||||||
 | 
					      template = this.#templates.get(tpl);
 | 
				
			||||||
 | 
					      cache = template.cached;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    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 = { ...data, ...this.#globals, ...locals };
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const code = 'with(_data){const __html = [];' +
 | 
					      const code = 'with(_data){const __html = [];' +
 | 
				
			||||||
        '__html.push(\`' +
 | 
					        '__html.push(\`' +
 | 
				
			||||||
        tpl.replace(/[\t]/g, ' ')
 | 
					        this.getTemplate(file)
 | 
				
			||||||
          .replace(/'(?=[^\{]*}})/, '\t')
 | 
					          .replace(/[\t]/g, ' ')
 | 
				
			||||||
          .split('\`').join('\\\`')
 | 
					          .split('\`').join('\\\`')
 | 
				
			||||||
          .split('\t').join('\`')
 | 
					
 | 
				
			||||||
          .replace(/{{=(.+?)}}/g, '\`,$1,\`')
 | 
					          .replace(/{{--(.*?)--}}/g, "") // comments
 | 
				
			||||||
          .replace(/{{-(.+?)}}/g, '\`,this.escape($1),\`')
 | 
					          .replace(/{{(.+?)}}/g, '\`,$1,\`') // yield variable
 | 
				
			||||||
          .replace(/{{include (.*?)}}/g, (_, inc) => this.render(this.getTemplate(inc), data))
 | 
					          .replace(/{!!(.+?)!!}/g, '\`,this.escape($1),\`') // yield escaped variable
 | 
				
			||||||
          .replace(/{{each (?<key>.*) as (?<val>.*)}}/g, (_, key, val) => `\`);this.forEach(${key},(${val},key)=>{__html.push(\``)
 | 
					
 | 
				
			||||||
          .replace(/{{\/each}}/g, "\`);});__html.push(`")
 | 
					          .replace(/@js/g, "`);") // inject bare javascript
 | 
				
			||||||
          .split('{{').join('\`);')
 | 
					          .replace(/@endjs/g, ";__html.push(`")
 | 
				
			||||||
          .split('}}').join('__html.push(\`')
 | 
					
 | 
				
			||||||
        + '\`);return __html.join(\'\').replace(/\\n\\s*\\n/g, "\\n");}';
 | 
					          .replace(/@include\((.*?)\)/g, (_, inc) => this.render(inc, data)) // include template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .replace(/@for\((.*?)\)$/gm, `\`);for($1){__html.push(\``)
 | 
				
			||||||
 | 
					          .replace(/@endfor/g, `\`);}__html.push(\``)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .replace(/@each\((.*?) as (.*?)\)/g, `\`);this.forEach($1,($2,key)=>{__html.push(\``) // foreach for the poor
 | 
				
			||||||
 | 
					          .replace(/@endeach/g, "\`);});__html.push(`")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .replace(/@elseif\((.*?)\)(\)?)/g, `\`);}else if($1$2){__html.push(\``) // if lol
 | 
				
			||||||
 | 
					          .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({
 | 
					      return (new Function("_data", code)).bind({
 | 
				
			||||||
        escape: this.escape,
 | 
					        escape: this.escape,
 | 
				
			||||||
        forEach: this.forEach
 | 
					        forEach: this.forEach
 | 
				
			||||||
      })(data);
 | 
					      })(data);
 | 
				
			||||||
    } catch(err) {
 | 
					    } catch(err) {
 | 
				
			||||||
      console.log(err);
 | 
					      console.log(file, err.message);
 | 
				
			||||||
      return err.message;
 | 
					      return (this.#debug ? `${err.message} in ${file}` : '');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  escape(str) {
 | 
					  escape(str) {
 | 
				
			||||||
    return (str + '')
 | 
					    return (str + '')
 | 
				
			||||||
      .replace(/&/g, '&')
 | 
					      .replace(/&/g, '&')
 | 
				
			||||||
@@ -45,6 +115,7 @@ export default class {
 | 
				
			|||||||
      .replace(/}/g, '}')
 | 
					      .replace(/}/g, '}')
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  forEach(o, f) {
 | 
					  forEach(o, f) {
 | 
				
			||||||
    if(Array.isArray(o))
 | 
					    if(Array.isArray(o))
 | 
				
			||||||
      o.forEach(f);
 | 
					      o.forEach(f);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user