flickerjs
Version:
Super fast and simple web framework for node.js (v6.0.0)
354 lines (346 loc) • 12.9 kB
JavaScript
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const querystring = require('querystring');
const colors = require('colors');
const CoolRequest = require('./request');
const CoolResponse = require('./response');
const aCoolRouter = require('./router');
function flickerApp(){
var App = (request,response) => {
App.handle(request,response);
}
App.locals = {};
App.stack = [];
App.settings = {};
App.set = (key,val) => {
App.settings[key] = val || undefined;
return App;
};
App.get = (key) => {
return App.settings[key] || undefined;
return App;
};
App.defaultSettings = () => {
App.set('template','pug');
App.set('static dir',__dirname + '/public');
App.set('views dir',__dirname + '/views');
App.set('env','development');
return App;
};
App.handle = (request,response) => {
var req = Object.assign(request,CoolRequest);
var res = Object.assign(response,CoolResponse);
req.res = res;
res.req = req;
res.app = req.app = App;
req.on('error', (err) => {
console.log('Problem with request ${err.message}');
});
req.path = url.parse(req.url).pathname;
req.path = req.path.toLowerCase();
if(req.url.length > 2){
if(req.url[req.url.length -1] === '/'){ req.url = req.url.slice(0,-1);}
if(req.path[req.path.length -1] === '/'){ req.path = req.path.slice(0,-1);}
}
req.originalUrl = req.url;
var bodyplus = querystring.parse(req.query());
req.body = Object.assign(bodyplus,req.body);
var idx = 0;
var requested = false;
next();
function next(err){
if(!err){
var layer = App.stack[idx++] || undefined;
if(!layer){
res.status(404).send(`CANNOT ${req.method} ${req.path}`);
return;
}
else{
App.next = next;
if(layer.router){
if(layer.path === '*'){
req.origin = '/';
req.resource = req.path;
try{
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
else{
var origin = req.path.substr(0,layer.path.length) || '/';
if(origin == '/'){
var resource = req.path;
}
else{
resource = req.path.substr(layer.path.length,req.path.length) || '/';
}
if(origin === layer.path){
try{
req.origin = origin;
req.resource = resource;
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
else{
return next();
}
}
}
else{
if(layer.regex){
if( (layer.regex.exp.test(req.path)) && (layer.method == req.method || layer.method == '*') ){
let values = [];
let re = /\/([A-Za-z0-9_-]+)/g;
let myArray = req.path.match(re);
myArray.forEach( (val) => {
let foo = val.slice(1);
foo = parseInt(foo) || foo;
values.push(foo);
});
for(let i = 0; i < values.length; i++){
if(layer.regex.comp.indexOf(values[i]) !== -1){
values.splice(i,1);
}
}
layer.regex.values = values;
for(let i = 0; i< layer.regex.keys.length ; i++){
let key = layer.regex.keys[i];
let val = layer.regex.values[i];
req.params[key] = val;
}
try{
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
else{
return next();
}
}
else{
if(layer.path === req.path && layer.path !== '*'){
if(layer.method != '*'){
if(layer.method == req.method){
requested = true;
try{
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
else{
return next();
}
}
else{
requested = true;
try{
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
}
else if(!requested && layer.path == "*"){
try{
return layer.handler(req,res,next);
}
catch(err){
next(err);
}
}
else{
return next();
}
}
}
}
}
else{
return App.err(req,res,next,err);
}
}
};
App.Router = () => {
return new aCoolRouter();
};
App.err = function (req,res,next,err){
if(typeof err === 'string'){
err = new Error(err);
}
let fn = App.stack[App.stack.length -1].handler;
if(fn.length == 4){
return fn(req,res,next,err);
}
else{
res.send(err.stack);
}
var errstr = err.toString();
fs.readFile('./flicker.log', (err,file) => {
var date = new Date();
let content = file + '\n' + new Date() + ' ---> ' + errstr;
fs.writeFile('./flicker.log', content,
(err) => {
if (err) throw err;
});
});
};
App.getViewExt = () => {
switch(App.get('template')){
case 'pug':
return '.pug';
case 'html':
case 'swig':
case 'whiskers':
return '.html';
case 'jade':
return '.jade';
case 'ejs':
return '.ejs';
case 'blade':
return '.blade';
case 'haml':
case 'haml-coffee':
return '.haml';
case 'pug':
return '.pug';
case 'marko':
return '.marko';
case 'handlebars':
case '.hbs':
return '.hbs';
case 'mustache':
return '.mustache';
case 'hjs':
return '.hjs';
default:
return '.html';
}
}
App.add = config => {
if(Array.isArray(config)){
for(let i = 0 ; i < config.length ; i++){
App.add(config[i]);
}
}
else{
let fn = () => {};
let regex = false;
let hasRouter = false;
let method = '*';
let path = '*';
if(typeof config === 'function'){
fn = config;
}
else{
fn = config.handler;
if(config.method){
method = config.method.toUpperCase();
}
if(config.url){
path = config.url;
if(path.indexOf(':') != -1){ /* is like /user/:id */
regex = {};
regex.keys = [];
regex.exp = App.normalize(path,regex.keys);
let comp = [];
let re = /\/(\w+)/g;
let myArray = path.match(re);
myArray.forEach( (val) => {
let foo = val.slice(1);
comp.push(foo);
});
regex.comp = comp;
}
}
}
if(Array.isArray(fn)){
for(let i = 0 ; i < fn.length ; i++){
var arrhasRouter = false;
if(fn[i].add){ // is a router
arrhasRouter = true;
}
App.stack.push({path: path, handler: fn[i], router: arrhasRouter, method: method,regex: regex});
}
}
else{
if(fn.add){ // is a router
hasRouter = true;
}
App.stack.push({path: path, handler: fn, router: hasRouter, method: method,regex: regex});
}
}
return App;
};
App.normalize = function(path,keys) {
if (path instanceof RegExp) return path
return new RegExp('^' + App.escapeRegexp(App.normalizePath(path), '.')
.replace(/\*/g, '(.+)')
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
keys.push(key)
return '(?:' + c + '([^\/]+))?'
})
.replace(/:(\w+)/g, function(_, key){
keys.push(key)
return '([^\/]+)'
}) + '$', 'i')
};
App.escapeRegexp = function(string, chars) {
var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\')
return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1')
}
App.normalizePath = function(path) {
return path.replace(/[\s\/]*$/g, '')
}
App.listen = (port,callback) => {
var server = http.createServer();
server.timeout = 0;
server.on('request',App);
server.on('clientError', (err, socket) => {
socket.end("HTTP/1.1 404 Bad request \r\n\r\n");
});
server.listen(port || 3000);
console.log(`${colors.green('[*] Server Listening on port ')} ${colors.blue(port)}\n`);
callback && callback();
return server;
};
App.serveStatic = () => {
return (req,res,next) => {
if(req.path.indexOf('.') != -1){
var realpath = App.get('static dir') + req.path;
fs.exists(realpath, (exists) => {
if(exists){
var tunnel = fs.createReadStream(realpath);
tunnel.pipe(res);
}
else{
next();
}
});
}
else{
next();
}
};
};
//initial middleware
App.add((req,res,next) => {
next();
})
// default settings
.defaultSettings();
return App;
};
module.exports = flickerApp;