├── .gitignore ├── .travis.yml ├── GUIDE.md ├── HISTORY.md ├── LICENSE ├── README.md ├── appveyor.yml ├── assets └── flickerjs.png ├── examples ├── api.js ├── app.js ├── basic.js ├── next.js ├── public │ ├── css │ │ └── style.css │ ├── favicon.ico │ ├── js │ │ └── index.js │ └── test.json ├── router.js ├── routers │ ├── bar.js │ ├── blog.js │ └── home.js └── views │ ├── err.pug │ ├── index.pug │ └── layout.pug ├── index.js ├── lib ├── application.js ├── flicker.js ├── request.js ├── response.js └── router.js ├── package.json └── test ├── index.js ├── public ├── css │ └── style.css ├── favicon.ico ├── js │ └── index.js └── test.json └── views └── index.pug /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | sudo: false 5 | script: "npm test" 6 | -------------------------------------------------------------------------------- /GUIDE.md: -------------------------------------------------------------------------------- 1 | After you install the module, require it: 2 | ```javascript 3 | const flicker = require('fickerjs'); 4 | ``` 5 | initialize your app: 6 | ```javascript 7 | let app = flicker(); 8 | ``` 9 | 10 | If you want, change the default configs: 11 | ```javascript 12 | app 13 | .set('template','pug') /* view engine */ 14 | .set('static dir','./public') /* static content directory (.css, .js, .json...)*/ 15 | .set('views dir','./views'); /* views directory ( .pug, .haml, .html) */ 16 | app.locals.foo = 'bar'; /* app.locals is an object that you can use (and call) it everywhere (middlewares, routers, renders...)*/ 17 | ``` 18 | Now, you can add the middlewares you want 19 | ```javascript 20 | app.add(compress()) /* data compress*/ 21 | .add(favicon('./public/favicon.ico')) /* serve favicon and cache it*/ 22 | .add(app.serveStatic('./public')) /* serve static content */ 23 | .add(bodyParser.json()) /* data parser to req.body */ 24 | .add(bodyParser.urlencoded({ extended: true })) /* same above */ 25 | .add(cookieParser()) /* cookies parser to req.cookies */ 26 | ``` 27 | you can set routers for a path (or all) and a method through the 'app.add' method. 28 | 29 | | Param | Object | 30 | |-----|---------| 31 | | req | Request. | 32 | | res | Response. | 33 | | next | Next middleware to call. | 34 | 35 | ```javascript 36 | app.add( 37 | (req,res,next) => { 38 | res.render("index",{ title: 'My Title Page'}); 39 | } 40 | ); 41 | ``` 42 | ##Response 43 | instance of http.ServerResponse. 44 | ```javascript 45 | res.send('foo'); /* => send 'foo' */ 46 | res.status(404); // response status is 404 47 | res.status(404).send('Not Found'); /* => send error 404 and 'Not Found' */ 48 | res.sendStatus(404); /* => same above */ 49 | res.json({'foo': 'bar'}) /* => send '{'foo':'bar'}'*/ 50 | res.sendFile('/test.json') /* => send the content of file /public/test.json (or your static dir)*/ 51 | res.render('index',{foo: 'bar',bar: 'foo'}) /* => send the view index.pug (default, or your views engine)*/ 52 | res.redirect('/foo') /* => redirect users to /foo */ 53 | res.locals /* => is similar to app.locals but only lives in current request (you can refresh it inn each request through middlewares) */ 54 | ``` 55 | 56 | ##Router 57 | Its a handler for your paths. You can to nest routers on the app. 58 | ```javascript 59 | let router = app.Router(); 60 | 61 | router 62 | .add({ 63 | url: '/path', 64 | method: 'GET', 65 | handler: (req,res,next) => { /* anything */} 66 | }) 67 | .add({ 68 | url: '/path', 69 | method: 'POST', 70 | handler: (req,res,next) => { /* anything */} 71 | }) 72 | .add({ 73 | url: '/path', 74 | method: 'PUT', 75 | handler: (req,res,next) => { /* anything */} 76 | }) 77 | .add({ 78 | url: '/path', 79 | method: 'DELETE', 80 | handler: (req,res,next) => { /* anything */} 81 | }) 82 | .add({ 83 | url: '/path', 84 | method: 'PUT', 85 | handler: (req,res,next) => { /* anything */} 86 | }) 87 | .add({ 88 | url: '/user/:id', 89 | handler: (req,res,next) => { /* req.params.id */} 90 | }) 91 | 92 | /* incorpore to your app */ 93 | app 94 | .add({ 95 | url: '/foo', 96 | handler: router 97 | }) 98 | ``` 99 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 2.0.4 2 | ==== 3 | * [Add] views error handler 4 | 5 | 2.0.3 6 | ==== 7 | * setting default directories with `app.set(___dirname + '/dir') ` 8 | 9 | 2.0.1 10 | ==== 11 | * Robust nested routers 12 | * auto Content-Length in `res.send` 13 | 14 | 2.0.0 15 | ==== 16 | * `app.add` also receives an array of handlers 17 | * [Rename] `app.to` to `app.add` 18 | 19 | 1.1.0 20 | ==== 21 | * [Set] morgan as default logger 22 | * [Add] `Response.header` 23 | * [Add] `Response.set` 24 | * [Add] `Response.get` 25 | 26 | 1.0.1 27 | ==== 28 | * [Fixed] bug when router path was `/` 29 | 30 | 1.0.0 31 | ==== 32 | * [Rename] `app.use` to `app.to` 33 | * [Update] `Response.json` 34 | 35 | 0.1.7 36 | ==== 37 | * [Add] promises in `app.use` 38 | * [Add] `flicker-easy` generator 39 | 40 | 0.1.4 41 | ==== 42 | * [Add] Error handler 43 | 44 | 0.1.3 45 | ==== 46 | * [Add] `res.params` in path. Ex: `/user/:id` 47 | 48 | 0.1.1 49 | ==== 50 | * Default middleare when app stack was empty 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Christopher Ventura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![logo](/assets/flickerjs.png)](https://www.npmjs.com/package/flickerjs) 2 | A Super fast and simple web framework for [node.js](http://nodejs.org/). 3 | 4 | [![Build Status](https://travis-ci.org/FlickerStudio/flickerjs.svg?branch=master)](https://travis-ci.org/FlickerStudio/flickerjs) [![Dependency Status](https://david-dm.org/flickerstudio/flickerjs.svg)](https://david-dm.org/flickerstudio/flickerjs) [![Build status](https://ci.appveyor.com/api/projects/status/qgxx72iq7wiluutm?svg=true)](https://ci.appveyor.com/project/flickerapps/flickerjs) 5 | ```javascript 6 | const flicker = require('flickerjs'); 7 | var app = flicker(); 8 | app 9 | .add({ 10 | url: '/', 11 | handler: (req, res) => { 12 | res.send('Hello Flicker.js'); 13 | } 14 | }) 15 | .listen(3000); 16 | 17 | ``` 18 | Install 19 | ==== 20 | ``` 21 | $ npm install flickerjs 22 | ``` 23 | 24 | Usage 25 | ==== 26 | 27 | ###[Documentacion del proyecto en español](https://github.com/flickerapps/flickerjs) 28 | 29 | via [flicker-easy](https://www.npmjs.com/package/flicker-easy) package. 30 | Generating the app: 31 | ``` 32 | $ flickerjs todolist 33 | ``` 34 | Or 35 | ``` 36 | $ flicker todolist /mydir 37 | $ cd mydir 38 | ``` 39 | Install dependencies: 40 | ``` 41 | $ npm install 42 | ``` 43 | Starting your server 44 | ``` 45 | $ npm start 46 | ``` 47 | 48 | Examples 49 | ==== 50 | To view examples clone the repo and run the example you want. 51 | List of example files: 52 | 53 | * api.js 54 | * app.js 55 | * basic.js 56 | * router.js 57 | * next.js 58 | 59 | ``` 60 | $ git clone https://github.com/flickerstudio/flickerjs.git 61 | $ cd flickerjs 62 | $ npm install 63 | $ node /examples/[file] 64 | 65 | ``` 66 | 67 | Tests 68 | ==== 69 | To run tests, after you clone the repo: 70 | ``` 71 | $ npm install 72 | $ npm test 73 | ``` 74 | 75 | Mini Doc 76 | ==== 77 | [Flicker Quick Guide](GUIDE.md) 78 | 79 | 80 | 81 | Contributors 82 | ==== 83 | Thanks to: 84 | * [Christopher Ventura](http://github.com/chrisvent) 85 | * [Dawson Botsford](http://github.com/dawsonbotsford) 86 | 87 | License 88 | ==== 89 | [MIT](LICENSE) 90 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Modified template from https://www.appveyor.com/docs/lang/nodejs-iojs 2 | 3 | # Test against this version of Node.js 4 | environment: 5 | nodejs_version: "6" 6 | 7 | # Get the latest stable version of Node 0.STABLE.latest (runs after repo cloning) 8 | install: 9 | # Get the latest stable version of Node.js or io.js 10 | - ps: Install-Product node $env:nodejs_version 11 | # install modules 12 | - npm install 13 | 14 | # Post-install test scripts. 15 | test_script: 16 | # Output useful info for debugging. 17 | - node --version 18 | - npm --version 19 | # run tests 20 | - npm test 21 | 22 | # Don't actually build. 23 | build: off 24 | -------------------------------------------------------------------------------- /assets/flickerjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venturachrisdev/flickerjs/e8f4bde0a5bc8073cebfe78fdbd1e3b4d6689b7d/assets/flickerjs.png -------------------------------------------------------------------------------- /examples/api.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../'); 2 | const bodyParser = require('body-parser'); 3 | const compress = require('compression'); 4 | const logger = require('morgan'); 5 | let app = flicker(); 6 | 7 | app 8 | .add(compress()) 9 | .add(bodyParser.json()) 10 | .add(bodyParser.urlencoded({ extended: true })) 11 | .add(logger('dev')); 12 | 13 | let api = app.Router(); 14 | 15 | app.locals.todos = [ 16 | { description: "Lorem 0" }, 17 | { description: "Lorem 1" }, 18 | { description: "Lorem 2" }, 19 | { description: "Lorem 3" }, 20 | { description: "Lorem 4" }, 21 | { description: "Lorem 5" } 22 | ]; 23 | 24 | api 25 | .add({ 26 | url:'/todos', 27 | method: 'GET', 28 | handler: (req,res,next) => { /* return todos */ 29 | res.json(app.locals.todos); 30 | } 31 | }) 32 | .add({ 33 | url: '/todos/:todo', 34 | method: 'GET', 35 | handler: (req,res,next) => { /* return todo */ 36 | if(req.params.todo >= app.locals.todos.length){ 37 | next(); 38 | } 39 | else{ 40 | res.json(app.locals.todos[req.params.todo]); 41 | } 42 | } 43 | }) 44 | .add({ 45 | url: '/todos', 46 | method: 'POST', 47 | handler: (req,res,next) => { /* insert todo */ 48 | app.locals.todos.push(req.body.todo); 49 | res.json(app.locals.todos) 50 | } 51 | }) 52 | .add({ 53 | url:'/todos/:todo', 54 | method: 'DELETE', 55 | handler: (req,res,next) => { /* delete todo */ 56 | if(req.params.todo >= app.locals.todos.length){ 57 | next(); 58 | } 59 | else{ 60 | app.locals.todos.splice(req.params.todo,1); 61 | res.json(app.locals.todos); 62 | } 63 | } 64 | }) 65 | .add({ 66 | url: '/todos/:todo', 67 | method: 'PUT', 68 | handler: (req,res,next) => { /* edit todo */ 69 | if(req.params.todo >= app.locals.todos.length){ 70 | next(); 71 | } 72 | else{ 73 | app.locals.todos[req.params.todo] = req.body.todo; 74 | res.json(app.locals.todos) 75 | } 76 | } 77 | }) 78 | 79 | app 80 | .add({ 81 | url: '/api', 82 | handler: api /* include the router */ 83 | }) 84 | .add({ 85 | url: '/', 86 | handler: (req,res,next) => { 87 | res.redirect("/api/todos"); 88 | } 89 | }) 90 | .add((req,res,next) => { 91 | res.json({}); // return a empty json 92 | }) 93 | .listen(3000); /* listen */ 94 | -------------------------------------------------------------------------------- /examples/app.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../'); 2 | const favicon = require('serve-favicon'); 3 | const cookieParser = require('cookie-parser'); 4 | const bodyParser = require('body-parser'); 5 | const compress = require('compression'); 6 | const logger = require('morgan'); 7 | let app = flicker(); 8 | let fooRouter = app.Router(); 9 | let barRouter = require('./routers/bar.js'); // external router file 10 | 11 | app.set('template','pug') 12 | .set('static dir',__dirname + '/public') 13 | .set('views dir',__dirname + '/views') 14 | // .add('env','production'); 15 | .add(compress()) 16 | .add(logger('dev')) 17 | // .add(favicon( __dirname + '/public/favicon.ico')) 18 | .add(app.serveStatic()) 19 | .add(bodyParser.json()) 20 | .add(bodyParser.urlencoded({ extended: true })) 21 | .add(cookieParser()); 22 | 23 | 24 | // inherited in renders 25 | app.locals.year = 2016; 26 | 27 | app 28 | .add( 29 | (req,res,next) => { 30 | // inherited in renders 31 | res.locals.author = "Flicker.js"; 32 | next(); 33 | } 34 | ); 35 | 36 | 37 | fooRouter 38 | .add({ 39 | url: '/', 40 | method: 'GET', 41 | handler: (req,res,next) => { 42 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 43 | } 44 | }) 45 | .add({ 46 | url: '/bar', 47 | method: 'GET', 48 | handler: (req,res,next) => { 49 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 50 | } 51 | }) 52 | 53 | barRouter 54 | .add({ 55 | url: '/user/:id', 56 | method: 'GET', 57 | handler: (req,res,next) => { 58 | res.send(req.params.id); 59 | } 60 | }) 61 | 62 | fooRouter 63 | .add({ 64 | url: '/bar2', 65 | handler: barRouter 66 | }) 67 | app 68 | .add({ 69 | url: '/foo', 70 | handler: fooRouter 71 | }) 72 | .add({ 73 | url: '/bar', 74 | handler: barRouter 75 | }) 76 | .add({ 77 | url: '/', 78 | handler: (req,res,next) => { 79 | res.render('index',{title: 'Welcome to Flicker.js'}); 80 | } 81 | }) 82 | .add({ 83 | url: '/test', 84 | handler: (req,res,next) => { 85 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 86 | } 87 | }) 88 | 89 | .add({ 90 | url: '/blog', 91 | handler: (req,res,next) => { 92 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 93 | } 94 | }) 95 | .add({ 96 | url: '/user/:id', 97 | handler: (req,res,next) => { 98 | res.send(req.params.id); 99 | } 100 | }) 101 | 102 | 103 | 104 | .add({ 105 | handler:[ 106 | (req,res,next) => { 107 | var err = new Error('Not Found'); 108 | err.status = 404; 109 | next(err); 110 | }, 111 | (req,res,next,err) => { 112 | if(app.get('env') == 'production'){ 113 | err.stack = ""; 114 | } 115 | res.status(err.status || 500).render("err",{ title: err.message, error: err}); 116 | } 117 | ] 118 | }) 119 | .listen(3000, () => { 120 | console.log('Running...'); 121 | }); 122 | -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../'); 2 | let app = flicker(); 3 | 4 | app.add(app.serveStatic()); 5 | // inherited in renders 6 | app.locals.year = 2016; 7 | 8 | app 9 | .add({ 10 | url: '/', 11 | method: 'GET', 12 | handler: [ 13 | (req,res,next) => { 14 | app.locals.author = "Flicker.js 2"; 15 | next(); 16 | }, 17 | (req,res,next) => { 18 | res.render('index',{title: 'Welcome to Flicker.js'}); 19 | } 20 | ] 21 | }) 22 | .add({ 23 | url: '/user/:id', 24 | handler: (req,res,next) => { 25 | res.send(req.params.id); 26 | } 27 | }) 28 | 29 | .add({ 30 | url: '/blog/:blog/cat/:cat', 31 | handler: (req,res,next) => { 32 | res.json(req.params); 33 | } 34 | }) 35 | .listen(3000); 36 | -------------------------------------------------------------------------------- /examples/next.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../'); 2 | let app = flicker(); 3 | 4 | app.add( 5 | (req,res,next) => { 6 | console.log('Atention: '); 7 | next(); 8 | } 9 | ) 10 | .add({ 11 | handler:[ 12 | (req,res,next) => { 13 | console.log('You should see this'); 14 | res.end(); 15 | }, 16 | (req,res,next) => { 17 | console.log('but, You do not should see this'); 18 | res.end(); 19 | } 20 | 21 | ] 22 | }) 23 | .listen(3000); 24 | -------------------------------------------------------------------------------- /examples/public/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | box-sizing: border-box; 5 | font-family: "Helvetica",Arial,sans-serif; 6 | background-color: #E1DDD7; 7 | color: #009E60; 8 | font-size: 1.1rem; 9 | } 10 | h1 { 11 | padding: 50px 50px 0; 12 | font-size: 2.3rem; 13 | } 14 | 15 | p { 16 | padding: 0px 60px; 17 | } 18 | 19 | footer { 20 | padding: 60px; 21 | } 22 | 23 | span { 24 | font-size: 0.9rem; 25 | color: #818181; 26 | } 27 | pre { 28 | padding: 10px 60px; 29 | font-size: 1rem; 30 | color: #7B7B7B; 31 | } 32 | 33 | h2{ 34 | padding-left: 60px; 35 | } 36 | -------------------------------------------------------------------------------- /examples/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venturachrisdev/flickerjs/e8f4bde0a5bc8073cebfe78fdbd1e3b4d6689b7d/examples/public/favicon.ico -------------------------------------------------------------------------------- /examples/public/js/index.js: -------------------------------------------------------------------------------- 1 | var element = document.getElementById('flicker'); 2 | -------------------------------------------------------------------------------- /examples/public/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "lorem": "ipsum" 4 | } 5 | -------------------------------------------------------------------------------- /examples/router.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../'); 2 | const favicon = require('serve-favicon'); 3 | const cookieParser = require('cookie-parser'); 4 | const logger = require('morgan'); 5 | const bodyParser = require('body-parser'); 6 | const compress = require('compression'); 7 | //middlewares 8 | 9 | let app = flicker(); 10 | let homeRouter = require('./routers/home'); 11 | 12 | app.set('template','pug') 13 | .set('static dir',__dirname + '/public') 14 | .set('views dir',__dirname + '/views') 15 | // .set('env','production'); 16 | .add(compress()) 17 | .add(logger('dev')) 18 | // .add(favicon(__dirname + '/public/favicon.ico')); 19 | .add(app.serveStatic()) 20 | .add(bodyParser.json()) 21 | .add(bodyParser.urlencoded({ extended: true })) 22 | .add(cookieParser()) 23 | 24 | 25 | // inherited in renders 26 | app.locals.year = 2016; 27 | app.locals.site_title = 'MyFlicker.com'; 28 | app.locals.site_description = 'Flicker.js demo'; 29 | 30 | app 31 | .add( 32 | (req,res,next) => { // custom middleware 33 | // inherited in renders 34 | res.locals.author = "Flicker.js"; 35 | next(); 36 | } 37 | ) 38 | .add({ 39 | url: '/', 40 | handler: homeRouter 41 | }) 42 | .add( 43 | (req,res,next) => { 44 | var err = new Error('Not Found'); 45 | err.status = 404; 46 | next(err); 47 | } 48 | ) 49 | 50 | .add( 51 | (req,res,next,err) => { 52 | if(app.get('env') == 'production'){ 53 | err.stack = ""; 54 | } 55 | res.status(err.status || 500).render("err",{ title: err.message, error: err}); 56 | } 57 | ) 58 | 59 | .listen(3000); 60 | -------------------------------------------------------------------------------- /examples/routers/bar.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../../'); 2 | 3 | var router = flicker().Router(); 4 | 5 | router 6 | .add({ 7 | url: '/foo', 8 | method: 'GET', 9 | handler: (req,res,next) => { 10 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 11 | } 12 | }) 13 | .add({ 14 | url: '/', 15 | method: 'GET', 16 | handler: (req,res,next) => { 17 | res.render('index',{title: 'Welcome to Flicker.js', message: 'Hello, I`m ' + req.url}); 18 | } 19 | }) 20 | 21 | module.exports = router; 22 | -------------------------------------------------------------------------------- /examples/routers/blog.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../../'); 2 | var router = flicker().Router(); 3 | router 4 | .add({ 5 | url: '/', 6 | method: 'GET', 7 | handler: (req,res,next) => { 8 | var alert = { 9 | msg: 'No Posts found.', 10 | type: 'info' 11 | } 12 | res.render('index', {title: 'Blog', alert: alert} ); 13 | } 14 | }) 15 | .add({ 16 | url: '/:slug', 17 | method: 'GET', 18 | handler: (req,res,next) => { 19 | res.render('index',{title: req.params.slug}); 20 | } 21 | }) 22 | 23 | module.exports = router; 24 | -------------------------------------------------------------------------------- /examples/routers/home.js: -------------------------------------------------------------------------------- 1 | const flicker = require('../../'); 2 | var router = flicker().Router(); 3 | var blogRouter = require('./blog'); 4 | 5 | router 6 | .add({ 7 | url: '/', 8 | method: 'GET', 9 | handler: (req,res,next) => { 10 | res.render('index',{title: 'Welcome to Flicker.js', message: 'FlickerJS is Running'}); 11 | } 12 | }) 13 | .add({ 14 | url: '/blog', 15 | method: 'GET', 16 | handler: blogRouter 17 | }) 18 | .add({ 19 | url: '/login', 20 | method: 'GET', 21 | handler: (req,res,next) => { 22 | var alert = { 23 | msg: 'Incorrect Username or Password', 24 | type: 'error' 25 | } 26 | res.render('index',{title: 'Sign In'}); 27 | } 28 | }) 29 | 30 | module.exports = router; 31 | -------------------------------------------------------------------------------- /examples/views/err.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | block content 3 | h1= error.message 4 | h2!= error.status 5 | pre!= error.stack 6 | -------------------------------------------------------------------------------- /examples/views/index.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | block content 3 | h1 #{title} 4 | if message 5 | p!= message 6 | else 7 | p= 'Your App is running!' 8 | footer 9 | if author && year 10 | span!= 'Powered by ' + author + ' - ' + year 11 | -------------------------------------------------------------------------------- /examples/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | meta(charset='UTF-8') 5 | title= title 6 | link(rel='stylesheet' type='text/css' href='../../css/style.css') 7 | body 8 | block content 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Flicker.js web framework 3 | 4 | - Flicker Studio 5 | - Christopher Ventura 6 | - Leonardo Perez 7 | */ 8 | 9 | var flickerjs = require('./lib/flicker'); 10 | module.exports = flickerjs; 11 | -------------------------------------------------------------------------------- /lib/application.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const http = require('http'); 3 | const url = require('url'); 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | const querystring = require('querystring'); 7 | const colors = require('colors'); 8 | const CoolRequest = require('./request'); 9 | const CoolResponse = require('./response'); 10 | const aCoolRouter = require('./router'); 11 | 12 | 13 | function flickerApp(){ 14 | 15 | var App = (request,response) => { 16 | App.handle(request,response); 17 | } 18 | App.locals = {}; 19 | App.stack = []; 20 | App.settings = {}; 21 | 22 | App.set = (key,val) => { 23 | App.settings[key] = val || undefined; 24 | return App; 25 | }; 26 | App.get = (key) => { 27 | return App.settings[key] || undefined; 28 | return App; 29 | }; 30 | App.defaultSettings = () => { 31 | App.set('template','pug'); 32 | App.set('static dir',__dirname + '/public'); 33 | App.set('views dir',__dirname + '/views'); 34 | App.set('env','development'); 35 | return App; 36 | }; 37 | App.handle = (request,response) => { 38 | var req = Object.assign(request,CoolRequest); 39 | var res = Object.assign(response,CoolResponse); 40 | req.res = res; 41 | res.req = req; 42 | res.app = req.app = App; 43 | req.on('error', (err) => { 44 | console.log('Problem with request ${err.message}'); 45 | }); 46 | req.path = url.parse(req.url).pathname; 47 | req.path = req.path.toLowerCase(); 48 | if(req.url.length > 2){ 49 | if(req.url[req.url.length -1] === '/'){ req.url = req.url.slice(0,-1);} 50 | if(req.path[req.path.length -1] === '/'){ req.path = req.path.slice(0,-1);} 51 | } 52 | req.originalUrl = req.url; 53 | var bodyplus = querystring.parse(req.query()); 54 | req.body = Object.assign(bodyplus,req.body); 55 | var idx = 0; 56 | var requested = false; 57 | next(); 58 | function next(err){ 59 | if(!err){ 60 | var layer = App.stack[idx++] || undefined; 61 | if(!layer){ 62 | res.status(404).send(`CANNOT ${req.method} ${req.path}`); 63 | return; 64 | } 65 | else{ 66 | App.next = next; 67 | if(layer.router){ 68 | if(layer.path === '*'){ 69 | req.origin = '/'; 70 | req.resource = req.path; 71 | try{ 72 | return layer.handler(req,res,next); 73 | } 74 | catch(err){ 75 | next(err); 76 | } 77 | } 78 | else{ 79 | var origin = req.path.substr(0,layer.path.length) || '/'; 80 | if(origin == '/'){ 81 | var resource = req.path; 82 | } 83 | else{ 84 | resource = req.path.substr(layer.path.length,req.path.length) || '/'; 85 | } 86 | if(origin === layer.path){ 87 | try{ 88 | req.origin = origin; 89 | req.resource = resource; 90 | return layer.handler(req,res,next); 91 | } 92 | catch(err){ 93 | next(err); 94 | } 95 | } 96 | else{ 97 | return next(); 98 | } 99 | } 100 | } 101 | else{ 102 | if(layer.regex){ 103 | if( (layer.regex.exp.test(req.path)) && (layer.method == req.method || layer.method == '*') ){ 104 | let values = []; 105 | let re = /\/([A-Za-z0-9_-]+)/g; 106 | let myArray = req.path.match(re); 107 | myArray.forEach( (val) => { 108 | let foo = val.slice(1); 109 | foo = parseInt(foo) || foo; 110 | values.push(foo); 111 | }); 112 | for(let i = 0; i < values.length; i++){ 113 | if(layer.regex.comp.indexOf(values[i]) !== -1){ 114 | values.splice(i,1); 115 | } 116 | } 117 | layer.regex.values = values; 118 | for(let i = 0; i< layer.regex.keys.length ; i++){ 119 | let key = layer.regex.keys[i]; 120 | let val = layer.regex.values[i]; 121 | req.params[key] = val; 122 | } 123 | try{ 124 | return layer.handler(req,res,next); 125 | } 126 | catch(err){ 127 | next(err); 128 | } 129 | } 130 | else{ 131 | return next(); 132 | } 133 | } 134 | else{ 135 | if(layer.path === req.path && layer.path !== '*'){ 136 | if(layer.method != '*'){ 137 | if(layer.method == req.method){ 138 | requested = true; 139 | try{ 140 | return layer.handler(req,res,next); 141 | } 142 | catch(err){ 143 | next(err); 144 | } 145 | } 146 | else{ 147 | return next(); 148 | } 149 | } 150 | else{ 151 | requested = true; 152 | try{ 153 | return layer.handler(req,res,next); 154 | } 155 | catch(err){ 156 | next(err); 157 | } 158 | } 159 | } 160 | else if(!requested && layer.path == "*"){ 161 | try{ 162 | return layer.handler(req,res,next); 163 | } 164 | catch(err){ 165 | next(err); 166 | } 167 | } 168 | else{ 169 | return next(); 170 | } 171 | } 172 | } 173 | } 174 | } 175 | else{ 176 | return App.err(req,res,next,err); 177 | } 178 | } 179 | }; 180 | App.Router = () => { 181 | return new aCoolRouter(); 182 | }; 183 | App.err = function (req,res,next,err){ 184 | if(typeof err === 'string'){ 185 | err = new Error(err); 186 | } 187 | let fn = App.stack[App.stack.length -1].handler; 188 | if(fn.length == 4){ 189 | return fn(req,res,next,err); 190 | } 191 | else{ 192 | res.send(err.stack); 193 | } 194 | var errstr = err.toString(); 195 | fs.readFile('./flicker.log', (err,file) => { 196 | var date = new Date(); 197 | let content = file + '\n' + new Date() + ' ---> ' + errstr; 198 | fs.writeFile('./flicker.log', content, 199 | (err) => { 200 | if (err) throw err; 201 | }); 202 | }); 203 | }; 204 | App.getViewExt = () => { 205 | switch(App.get('template')){ 206 | case 'pug': 207 | return '.pug'; 208 | case 'html': 209 | case 'swig': 210 | case 'whiskers': 211 | return '.html'; 212 | case 'jade': 213 | return '.jade'; 214 | case 'ejs': 215 | return '.ejs'; 216 | case 'blade': 217 | return '.blade'; 218 | case 'haml': 219 | case 'haml-coffee': 220 | return '.haml'; 221 | case 'pug': 222 | return '.pug'; 223 | case 'marko': 224 | return '.marko'; 225 | case 'handlebars': 226 | case '.hbs': 227 | return '.hbs'; 228 | case 'mustache': 229 | return '.mustache'; 230 | case 'hjs': 231 | return '.hjs'; 232 | default: 233 | return '.html'; 234 | } 235 | } 236 | App.add = config => { 237 | if(Array.isArray(config)){ 238 | for(let i = 0 ; i < config.length ; i++){ 239 | App.add(config[i]); 240 | } 241 | } 242 | else{ 243 | let fn = () => {}; 244 | let regex = false; 245 | let hasRouter = false; 246 | let method = '*'; 247 | let path = '*'; 248 | if(typeof config === 'function'){ 249 | fn = config; 250 | } 251 | else{ 252 | fn = config.handler; 253 | if(config.method){ 254 | method = config.method.toUpperCase(); 255 | } 256 | if(config.url){ 257 | path = config.url; 258 | if(path.indexOf(':') != -1){ /* is like /user/:id */ 259 | regex = {}; 260 | regex.keys = []; 261 | regex.exp = App.normalize(path,regex.keys); 262 | let comp = []; 263 | let re = /\/(\w+)/g; 264 | let myArray = path.match(re); 265 | myArray.forEach( (val) => { 266 | let foo = val.slice(1); 267 | comp.push(foo); 268 | }); 269 | regex.comp = comp; 270 | } 271 | } 272 | } 273 | if(Array.isArray(fn)){ 274 | for(let i = 0 ; i < fn.length ; i++){ 275 | var arrhasRouter = false; 276 | if(fn[i].add){ // is a router 277 | arrhasRouter = true; 278 | } 279 | App.stack.push({path: path, handler: fn[i], router: arrhasRouter, method: method,regex: regex}); 280 | } 281 | } 282 | else{ 283 | if(fn.add){ // is a router 284 | hasRouter = true; 285 | } 286 | App.stack.push({path: path, handler: fn, router: hasRouter, method: method,regex: regex}); 287 | } 288 | } 289 | return App; 290 | }; 291 | App.normalize = function(path,keys) { 292 | if (path instanceof RegExp) return path 293 | return new RegExp('^' + App.escapeRegexp(App.normalizePath(path), '.') 294 | .replace(/\*/g, '(.+)') 295 | .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){ 296 | keys.push(key) 297 | return '(?:' + c + '([^\/]+))?' 298 | }) 299 | .replace(/:(\w+)/g, function(_, key){ 300 | keys.push(key) 301 | return '([^\/]+)' 302 | }) + '$', 'i') 303 | }; 304 | App.escapeRegexp = function(string, chars) { 305 | var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\') 306 | return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1') 307 | } 308 | 309 | App.normalizePath = function(path) { 310 | return path.replace(/[\s\/]*$/g, '') 311 | } 312 | App.listen = (port,callback) => { 313 | var server = http.createServer(); 314 | server.timeout = 0; 315 | server.on('request',App); 316 | server.on('clientError', (err, socket) => { 317 | socket.end("HTTP/1.1 404 Bad request \r\n\r\n"); 318 | }); 319 | server.listen(port || 3000); 320 | console.log(`${colors.green('[*] Server Listening on port ')} ${colors.blue(port)}\n`); 321 | callback && callback(); 322 | return server; 323 | }; 324 | 325 | App.serveStatic = () => { 326 | return (req,res,next) => { 327 | var staticregex = /^\/.+\.\w{1,4}$/; 328 | //if(req.path.indexOf('.') != -1){ 329 | if( staticregex.test(req.path)){ 330 | var realpath = App.get('static dir') + req.path; 331 | fs.exists(realpath, (exists) => { 332 | if(exists){ 333 | var tunnel = fs.createReadStream(realpath); 334 | tunnel.pipe(res); 335 | } 336 | else{ 337 | next(); 338 | } 339 | }); 340 | } 341 | else{ 342 | next(); 343 | } 344 | }; 345 | }; 346 | //initial middleware 347 | App.add((req,res,next) => { 348 | next(); 349 | }) 350 | // default settings 351 | .defaultSettings(); 352 | return App; 353 | }; 354 | 355 | module.exports = flickerApp; 356 | -------------------------------------------------------------------------------- /lib/flicker.js: -------------------------------------------------------------------------------- 1 | var app = require('./application'); 2 | module.exports = app; 3 | -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const url = require('url'); 3 | var Request = {}; 4 | Request.body = {}; 5 | 6 | /* 7 | * Params of Request Url 8 | * router url: => /user/:id 9 | * Request url: => /user/5 10 | * Request Params => {id: 5} 11 | */ 12 | Request.params = {}; 13 | 14 | /* 15 | * Parse the query of Request Url 16 | * Examples: 17 | * url => '/user/?id=4' 18 | * req.query() => 'id=4' 19 | */ 20 | Request.query = function(){ 21 | return url.parse(this.url).query || ''; 22 | }; 23 | 24 | module.exports = Request; 25 | 26 | -------------------------------------------------------------------------------- /lib/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const http = require('http'); 3 | const fs = require('fs'); 4 | const cons = require('consolidate'); 5 | 6 | var Response = {}; 7 | 8 | 9 | /* 10 | * Set a Status code 11 | * 12 | * @param {Number} code 13 | * @return ServerResponse 14 | * Examples: 15 | * res.status(404).send('Not Found'); 16 | * res.status(200).json({message: 'OK'}); 17 | * 18 | */ 19 | Response.status = function status(sts){ 20 | if(typeof sts != 'number'){ 21 | console.error('res.status() param must be a number, not a ' + typeof sts); 22 | sts = 302; 23 | } 24 | this.statusCode = sts; 25 | return this; 26 | }; 27 | 28 | /* 29 | * Redirect user (in browser) to an url 30 | * 31 | * @param {String} location 32 | * Examples: 33 | * res.redirect('/foo'); 34 | * res.redirect('http://google.com') 35 | */ 36 | 37 | Response.redirect = function(href){ 38 | this.status(302); 39 | this.setHeader('Location',href); 40 | this.end(); 41 | }; 42 | 43 | 44 | /* 45 | * Set a Status code and Send the appropiate message 46 | * @param {Number} statusCode 47 | * Example: 48 | * res.sendStatus(404) => Set status 404 and send "Not Found" 49 | * res.sendStatus(204) => Set status 202 and send "Accepted" 50 | * 51 | */ 52 | Response.sendStatus = function sendStatus(sts){ 53 | if(typeof sts !== 'number'){ 54 | console.error('res.sendStatus() param must be a number, not a ' + typeof str); 55 | sts = 302; 56 | } 57 | switch(sts){ 58 | case 200: 59 | this.statusMessage = "OK"; 60 | break; 61 | case 201: 62 | this.statusMessage = "Created"; 63 | break; 64 | case 202: 65 | this.statusMessage = "Accepted"; 66 | break; 67 | case 204: 68 | this.statusMessage = "No Content"; 69 | break; 70 | case 302: 71 | this.statusMessage = "Found"; 72 | break; 73 | case 304: 74 | this.statusMessage = "Not Modified"; 75 | break; 76 | case 400: 77 | this.statusMessage = "Bad Request"; 78 | break; 79 | case 401: 80 | this.statusMessage = "Unauthorized"; 81 | break; 82 | case 403: 83 | this.statusMessage = "Forbidden"; 84 | break; 85 | case 406: 86 | this.statusMessage = "Not Acceptable"; 87 | break; 88 | case 404: 89 | this.statusMessage = "Not Found"; 90 | break; 91 | case 415: 92 | this.statusMessage = "Unsoported Media Type"; 93 | break; 94 | case 429: 95 | this.statusMessage = "Too Many Requests"; 96 | break; 97 | case 431: 98 | this.statusMessage = "Request Header Fields Too Large"; 99 | break; 100 | case 500: 101 | this.statusMessage = "Internal Error"; 102 | break; 103 | default: 104 | this.statusMessage = "Error " + this.statusCode; 105 | }; 106 | this.status(sts).send(this.statusMessage); 107 | }; 108 | 109 | /* 110 | * Send content 111 | * @param {Number|String|Object} content 112 | * Examples: 113 | * res.send('Hello World') 114 | * res.send(200); 115 | * res.send({"foo":"bar"}) 116 | */ 117 | Response.send = function send(str){ 118 | if(typeof str == 'object'){ 119 | this.json(str); 120 | } 121 | else if(typeof str == 'number'){ 122 | if(str > 200 && str < 500){ 123 | this.sendStatus(str); 124 | } 125 | else{ 126 | this.send(str + ""); 127 | } 128 | } 129 | else{ 130 | let strlen = str.length; 131 | this.setHeader('Content-Length',strlen); 132 | this.preventStatus(200).end(str); 133 | } 134 | }; 135 | 136 | /* 137 | * Set Header of Response (content) 138 | * @param {String, String|Number} key,value 139 | * Example: 140 | * res.set("Content-Type","text/css") 141 | * res.header("Content-Length":24354) 142 | * res.set("Location","/foo") 143 | 144 | */ 145 | Response.header = Response.set = function(key,val){ 146 | this.setHeader(key,val); 147 | }; 148 | 149 | 150 | /* 151 | Get the Response headers 152 | * @params {String} Key (Header) 153 | * Example: 154 | * res.get("Content-Type") => "text/html" 155 | * res.get("Content-Length") => "376556" 156 | */ 157 | Response.get = function(key){ 158 | return this.getHeader(key); 159 | }; 160 | 161 | 162 | /* 163 | * Send a object in json format 164 | * @params {Object} JSON Object 165 | * Example: 166 | * res.json({foo: 'bar'}) 167 | * => 168 | { 169 | "foo":"bar" 170 | } 171 | */ 172 | Response.json = function json(obj){ 173 | if(typeof obj != 'object'){ 174 | console.error('res.json() param must be a object, not a ' + typeof obj); 175 | } 176 | this.preventStatus(200); 177 | this.setHeader("Content-Type","application/json"); 178 | this.write(JSON.stringify(obj,null,2)); 179 | this.end(); 180 | }; 181 | 182 | /* 183 | * Send the content of a file 184 | * @param {String} filename 185 | * Examples: 186 | * res.sendFile('./style.css') 187 | * res.sendFile('/myfile.json') 188 | */ 189 | Response.sendFile = function sendFile(filename){ 190 | var path; 191 | path = "./" + filename; // current folder; 192 | if(filename[0] === '.'){ 193 | path = filename; // is absolute 194 | } 195 | else if(filename[0] === '/'){ 196 | path = this.app.get('static dir') + filename; // public folder 197 | } 198 | let filecontent = fs.readFileSync(path); 199 | this.end(filecontent); 200 | }; 201 | 202 | 203 | /* 204 | * Render a template parsing with setted view engine 205 | * @params {String,Object} Filename, options (vars) 206 | * Examples: 207 | * res.render('index',{foo: "bar", title: 'Title Page'}) 208 | */ 209 | 210 | Response.render = function(filename,options){ 211 | options = Object.create(Object.assign(options,this.locals)); 212 | options = Object.create(Object.assign(options,this.app.locals)); 213 | options = options || {}; 214 | let path = this.app.get('views dir') + '/' + filename + this.app.getViewExt(); 215 | if(filename[0] == '.'){ 216 | path = filename + this.app.getViewExt(); 217 | } 218 | cons[this.app.get('template')](path,options, (err, html) => { 219 | if (err){ 220 | this.app.next(err); 221 | } 222 | else{ 223 | this.send(html); 224 | } 225 | }); 226 | }; 227 | 228 | /* 229 | * Set a statusCode as long as no one (no override the status coede) 230 | * @params {Number} statuscode 231 | * @return ServerResponse 232 | * Examples: (Similar to res.status) 233 | * res.preventStatus(200).send("Fooo") 234 | */ 235 | Response.preventStatus = function preventStatus(sts){ 236 | if(!this.statusCode){ 237 | this.status(sts); 238 | } 239 | return this; 240 | }; 241 | 242 | /* 243 | * Response locals 244 | * Examples: 245 | * res.locals.foo = bar; 246 | * res.json(res.locals) => {foo: "bar"} 247 | */ 248 | Response.locals = {}; 249 | 250 | module.exports = Response; 251 | -------------------------------------------------------------------------------- /lib/router.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | function Router(){ 3 | 4 | var routerProto = function(req,res,next){ 5 | if(routerProto.stack.length == 0){ 6 | res.status(404).send(`No routers found`); 7 | } 8 | var idxz = 0; 9 | var requested = false; 10 | internext(); 11 | function internext(err){ 12 | if(!err){ 13 | let layer = routerProto.stack[idxz++] || undefined; 14 | if(!layer){ 15 | return next(); 16 | } 17 | else{ 18 | if(layer.router){ 19 | var origin = req.resource.substr(0,layer.path.length) || '/'; 20 | if(origin === layer.path){ 21 | try{ 22 | req.origin = origin; 23 | req.resource = req.resource.substr(layer.path.length, req.resource.length) || '/'; 24 | return layer.handler(req,res,internext); 25 | } 26 | catch(err){ 27 | internext(err); 28 | } 29 | } 30 | else{ 31 | return internext(); 32 | } 33 | } 34 | else{ 35 | if(layer.regex){ 36 | if( (layer.regex.exp.test(req.resource)) && (layer.method == req.method || layer.method == '*') ){ 37 | let values = []; 38 | let re = /\/([A-Za-z0-9_-]+)/g; 39 | let myArray = req.resource.match(re); 40 | myArray.forEach( (val) => { 41 | let foo = val.slice(1); 42 | foo = parseInt(foo) || foo; 43 | values.push(foo); 44 | }); 45 | for(let i = 0; i < values.length; i++){ 46 | if(layer.regex.comp.indexOf(values[i]) !== -1){ 47 | values.splice(i,1); 48 | } 49 | } 50 | layer.regex.values = values; 51 | for(let i = 0; i< layer.regex.keys.length ; i++){ 52 | let key = layer.regex.keys[i]; 53 | let val = layer.regex.values[i]; 54 | req.params[key] = val; 55 | } 56 | try{ 57 | return layer.handler(req,res,internext); 58 | } 59 | catch(err){ 60 | internext(err); 61 | } 62 | } 63 | else{ 64 | return internext(); 65 | } 66 | } 67 | else{ 68 | if(layer.path === req.resource && layer.path != '*'){ 69 | if(layer.method != '*'){ 70 | if(layer.method == req.method){ 71 | requested = true; 72 | try{ 73 | return layer.handler(req,res,internext); 74 | } 75 | catch(err){ 76 | internext(err); 77 | } 78 | } 79 | else{ 80 | return internext(); 81 | } 82 | } 83 | else{ 84 | requested = true; 85 | try{ 86 | return layer.handler(req,res,internext); 87 | } 88 | catch(err){ 89 | internext(err); 90 | } 91 | } 92 | } 93 | else if(!requested && layer.path == "*"){ 94 | try{ 95 | return layer.handler(req,res,internext); 96 | } 97 | catch(err){ 98 | internext(err); 99 | } 100 | 101 | } 102 | else{ 103 | return internext(); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | else{ 110 | next(err); 111 | } 112 | } 113 | }; 114 | 115 | routerProto.stack = []; 116 | routerProto.add = config => { 117 | if(Array.isArray(config)){ 118 | for(let i = 0 ; i < config.length ; i++){ 119 | routerProto.add(config[i]); 120 | } 121 | } 122 | else{ 123 | let fn = () => {}; 124 | let regex = false; 125 | let hasRouter = false; 126 | let method = '*'; 127 | let path = '*'; 128 | if(typeof config === 'function'){ 129 | fn = config; 130 | } 131 | else{ 132 | fn = config.handler; 133 | if(config.method){ 134 | method = config.method.toUpperCase(); 135 | } 136 | if(config.url){ 137 | path = config.url; 138 | if(path.indexOf(':') != -1){ /* is like /user/:id */ 139 | regex = {}; 140 | regex.keys = []; 141 | regex.exp = routerProto.normalize(path,regex.keys); 142 | let comp = []; 143 | let re = /\/(\w+)/g; 144 | let myArray = path.match(re); 145 | if(myArray){ 146 | myArray.forEach( (val) => { 147 | let foo = val.slice(1); 148 | comp.push(foo); 149 | }); 150 | regex.comp = comp; 151 | } 152 | else{ 153 | regex.comp = []; 154 | } 155 | } 156 | } 157 | } 158 | if(Array.isArray(fn)){ 159 | for(let i = 0 ; i < fn.length ; i++){ 160 | var arrhasRouter = false; 161 | if(fn[i].add){ // is a router 162 | arrhasRouter = true; 163 | } 164 | routerProto.stack.push({path: path, handler: fn[i], router: arrhasRouter, method: method,regex: regex}); 165 | } 166 | } 167 | else{ 168 | if(fn.add){ // is a router 169 | hasRouter = true; 170 | } 171 | routerProto.stack.push({path: path, handler: fn, router: hasRouter, method: method,regex: regex}); 172 | } 173 | } 174 | return routerProto; 175 | }; 176 | routerProto.normalize = function(path,keys) { 177 | if (path instanceof RegExp) return path 178 | return new RegExp('^' + routerProto.escapeRegexp(routerProto.normalizePath(path), '.') 179 | .replace(/\*/g, '(.+)') 180 | .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){ 181 | keys.push(key) 182 | return '(?:' + c + '([^\/]+))?' 183 | }) 184 | .replace(/:(\w+)/g, function(_, key){ 185 | keys.push(key) 186 | return '([^\/]+)' 187 | }) + '$', 'i') 188 | }; 189 | routerProto.escapeRegexp = function(string, chars) { 190 | var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\') 191 | return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1') 192 | } 193 | 194 | routerProto.normalizePath = function(path) { 195 | return path.replace(/[\s\/]*$/g, '') 196 | }; 197 | 198 | return routerProto; 199 | } 200 | module.exports = Router; 201 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flickerjs", 3 | "version": "2.0.7", 4 | "description": "Super fast and simple web framework for node.js (v6.0.0)", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --reporter spec --timeout 5000" 8 | }, 9 | "author": "Flicker Studio", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/flickerstudio/flickerjs.git" 13 | }, 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">=6.0.0" 17 | }, 18 | "keywords": [ 19 | "flicker", 20 | "flickerjs", 21 | "server", 22 | "framework", 23 | "http", 24 | "web", 25 | "rest", 26 | "node", 27 | "router", 28 | "middleware", 29 | "fast", 30 | "js", 31 | "nodejs", 32 | "api" 33 | ], 34 | "dependencies": { 35 | "body-parser": "^1.15.0", 36 | "colors": "^1.1.2", 37 | "compression": "^1.6.1", 38 | "consolidate": "^0.14.1", 39 | "cookie-parser": "^1.4.1", 40 | "morgan": "^1.7.0", 41 | "flicker-easy": "latest", 42 | "pug": "latest" 43 | }, 44 | "devDependencies": { 45 | "chai": "^4.0.1", 46 | "mocha": "^3.0.0", 47 | "should": "^11.0.0", 48 | "supertest": "^3.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const should = require('should'); 3 | const assert = require('assert'); 4 | const request = require('supertest'); 5 | const http = require('http'); 6 | const bodyParser = require('body-parser'); 7 | const cookieParser = require('cookie-parser'); 8 | const flicker = require('../'); 9 | 10 | describe('App', 11 | () => { 12 | it('should be callable', 13 | () => { 14 | let app = flicker(); 15 | assert.equal(typeof app,'function'); 16 | } 17 | ); 18 | 19 | it('get and set', 20 | done => { 21 | let app = flicker() 22 | .add((req,res,next) => { 23 | app.set('foo','bar'); 24 | next(); 25 | }) 26 | .add((req,res,next) => { 27 | res.send(app.get('foo')); 28 | }); 29 | request(app) 30 | .get('/') 31 | .expect(200,'bar',done); 32 | } 33 | ); 34 | it('locals should be inherited', 35 | done => { 36 | let app = flicker() 37 | app.locals = { blog_title: 'Lorem Ipsum'}; 38 | app.add((req,res,next) => { 39 | app.locals.description = 'Lorem Ipsum dolor sit amet'; 40 | next(); 41 | }) 42 | .add({ 43 | url: '/pretty', 44 | handler: (req,res,next) => { 45 | res.send(app.locals.blog_title); 46 | } 47 | }); 48 | 49 | request(app) 50 | .get('/pretty') 51 | .expect(200,'Lorem Ipsum',done); 52 | } 53 | ); 54 | } 55 | ); 56 | describe('Router statusCode', 57 | () => { 58 | it('should be 200', 59 | done => { 60 | let app = flicker() 61 | .add((req,res,next) => { 62 | res.sendStatus(200); 63 | }); 64 | request(app) 65 | .get('/') 66 | .expect(200,done); 67 | 68 | }); 69 | it('should be 404', 70 | done => { 71 | let app = flicker() 72 | request(app) 73 | .get('/') 74 | .expect(404,done); 75 | 76 | }); 77 | it('should be 201', 78 | done => { 79 | let app = flicker() 80 | .add( 81 | (req,res) => { 82 | res.status(201).end(); 83 | } 84 | ); 85 | request(app) 86 | .get('/') 87 | .expect(201,done); 88 | } 89 | ); 90 | it('should be 500', 91 | done => { 92 | let app = flicker() 93 | .add((req,res) => { 94 | res.status(500).end(); 95 | }); 96 | request(app) 97 | .get('/') 98 | .expect(500,done); 99 | } 100 | ); 101 | } 102 | ); 103 | 104 | describe('Router delegation', 105 | () => { 106 | it('include router', 107 | done => { 108 | let app = flicker() 109 | let router = app.Router() 110 | .add({ 111 | url: '/blog', 112 | method: 'GET', 113 | handler: (req,res,next) => { 114 | res.send('im /blog'); 115 | } 116 | }) 117 | app.add({ 118 | url: "/", 119 | handler: router 120 | }) 121 | request(app) 122 | .get('/blog') 123 | .expect(200,done); 124 | } 125 | ); 126 | it('Nested routers', 127 | done => { 128 | let app = flicker() 129 | let router1 = app.Router(); 130 | let router2 = app.Router(); 131 | let router3 = app.Router(); 132 | 133 | router3 134 | .add({ 135 | url: '/wrong', 136 | handler: (req,res,next) => { 137 | res.send("Wrong"); 138 | } 139 | }) 140 | .add({ 141 | url: '/bar', 142 | handler: (req,res,next) => { 143 | res.send('Hello!'); 144 | } 145 | }); 146 | router2 147 | .add({ 148 | url: '/wrong', 149 | handler: (req,res,next) => { 150 | res.send("Wrong"); 151 | } 152 | }) 153 | .add({ 154 | url: '/foo', 155 | handler: router3 156 | }); 157 | router1 158 | .add({ 159 | url: '/wrong', 160 | handler: (req,res,next) => { 161 | res.send("Wrong"); 162 | } 163 | }) 164 | .add({ 165 | url: '/bar', 166 | handler: router2 167 | }); 168 | app 169 | .add({ 170 | url: '/wrong', 171 | handler: (req,res,next) => { 172 | res.send("Wrong"); 173 | } 174 | }) 175 | .add({ 176 | url: '/foo', 177 | handler: router1 178 | }) 179 | request(app) 180 | .get('/foo/bar/foo/bar') 181 | .expect(200,"Hello!",done); 182 | 183 | } 184 | ); 185 | } 186 | ); 187 | 188 | describe('Routing HTTP verbs', 189 | () => { 190 | it('middleares responses all verbs', 191 | done => { 192 | let app = flicker() 193 | .add( 194 | (req,res,next) => { 195 | res.send("Flicker"); 196 | } 197 | ); 198 | request(app) 199 | .patch('/') 200 | .expect(200,"Flicker",done); 201 | 202 | } 203 | ); 204 | 205 | it('GET', 206 | done => { 207 | let app = flicker() 208 | let router = app.Router(); 209 | router.add({ 210 | url: '/app', 211 | method: 'GET', 212 | handler: (req,res,next) => { 213 | res.send("Flicker"); 214 | } 215 | }) 216 | app.add(router); 217 | request(app) 218 | .get('/app') 219 | .expect(200,"Flicker",done); 220 | 221 | } 222 | ); 223 | 224 | it('POST', 225 | done => { 226 | let app = flicker(); 227 | let router = app.Router(); 228 | router.add({ 229 | url: '/app', 230 | method: 'POST', 231 | handler: (req,res,next) => { 232 | res.send("Flicker"); 233 | } 234 | }) 235 | app.add(router); 236 | request(app) 237 | .post('/app') 238 | .expect(200,"Flicker",done); 239 | 240 | } 241 | ); 242 | 243 | it('PUT', 244 | done => { 245 | let app = flicker(); 246 | let router = app.Router(); 247 | router.add({ 248 | url: '/app', 249 | method: 'PUT', 250 | handler: (req,res,next) => { 251 | res.send("Flicker"); 252 | } 253 | }) 254 | app.add(router); 255 | request(app) 256 | .put('/app') 257 | .expect(200,"Flicker",done); 258 | 259 | } 260 | ); 261 | 262 | it('DELETE', 263 | done => { 264 | let app = flicker(); 265 | let router = app.Router(); 266 | router.add({ 267 | url: '/app', 268 | method: 'DELETE', 269 | handler: (req,res,next) => { 270 | res.send("Flicker"); 271 | } 272 | }) 273 | app.add(router); 274 | request(app) 275 | .delete('/app') 276 | .expect(200,"Flicker",done); 277 | 278 | } 279 | ); 280 | 281 | it('PATCH', 282 | done => { 283 | let app = flicker() 284 | let router = app.Router(); 285 | router.add({ 286 | url: '/app', 287 | method: 'PATCH', 288 | handler: (req,res,next) => { 289 | res.send("Flicker"); 290 | } 291 | }) 292 | app.add(router); 293 | request(app) 294 | .patch('/app') 295 | .expect(200,"Flicker",done); 296 | 297 | } 298 | ); 299 | 300 | 301 | it('GET do not reponses for POST method', 302 | done => { 303 | let app = flicker() 304 | let router = app.Router(); 305 | router.add({ 306 | url: '/pretty', 307 | method: 'GET', 308 | handler: (req,res,next) => { 309 | res.send('I am a GET Response'); 310 | } 311 | }) 312 | app.add(router); 313 | request(app) 314 | .post('/pretty') 315 | .expect(404,done); 316 | } 317 | ); 318 | 319 | it('PUT do not reponses for DELETE method', 320 | done => { 321 | let app = flicker() 322 | let router = app.Router(); 323 | router.add({ 324 | url: '/pretty', 325 | method: 'PUT', 326 | handler: (req,res,next) => { 327 | res.send('I am a GET Response'); 328 | } 329 | }) 330 | app.add(router); 331 | request(app) 332 | .delete('/pretty') 333 | .expect(404,done); 334 | } 335 | ) 336 | } 337 | ); 338 | 339 | describe('Response Object', 340 | () => { 341 | it('end()', 342 | done => { 343 | let app = flicker() 344 | .add( 345 | (req,res) => { 346 | res.end("foo"); 347 | } 348 | ); 349 | 350 | request(app) 351 | .get('/') 352 | .expect(200,'foo',done); 353 | } 354 | ); 355 | it('send()', 356 | done => { 357 | let app = flicker() 358 | .add( 359 | (req,res) => { 360 | res.send("bar"); 361 | } 362 | ); 363 | 364 | request(app) 365 | .get('/') 366 | .expect(200,'bar',done); 367 | } 368 | ); 369 | 370 | it('status()', 371 | done => { 372 | let app = flicker() 373 | .add( 374 | (req,res) => { 375 | res.status(500).end("bar"); 376 | } 377 | ); 378 | 379 | request(app) 380 | .get('/') 381 | .expect(500,'bar',done); 382 | } 383 | ); 384 | 385 | it('preventStatus() do not override the current statusCode', 386 | done => { 387 | let app = flicker() 388 | .add( 389 | (req,res) => { 390 | res.status(200).preventStatus(404).end(); 391 | } 392 | ); 393 | 394 | request(app) 395 | .get('/') 396 | .expect(200,done); 397 | } 398 | ); 399 | it('json()', 400 | done => { 401 | let app = flicker() 402 | .add( 403 | (req,res) => { 404 | res.json({ foo: 'bar' } ); 405 | } 406 | ); 407 | 408 | request(app) 409 | .get('/') 410 | .expect(200,{ foo: 'bar' },done); 411 | } 412 | ); 413 | 414 | it('sendFile()', 415 | done => { 416 | let app = flicker() 417 | .add({ 418 | url: '/pretty', 419 | method: 'GET', 420 | handler: (req,res,next) => { 421 | res.sendFile('examples/public/test.json'); 422 | } 423 | }) 424 | request(app) 425 | .get('/pretty') 426 | .expect(200,done); 427 | } 428 | ); 429 | 430 | it('render()', 431 | done => { 432 | let app = flicker() 433 | .add({ 434 | url: '/pretty', 435 | method: 'GET', 436 | handler: (req,res,next) => { 437 | res.render('./test/views/index',{}); 438 | } 439 | }) 440 | request(app) 441 | .get('/pretty') 442 | .expect(200,done); 443 | } 444 | ); 445 | it('redirect()', 446 | done => { 447 | let app = flicker() 448 | .add({ 449 | url: '/pretty', 450 | method: 'GET', 451 | handler: (req,res,next) => { 452 | res.redirect('http://google.com/'); 453 | } 454 | }) 455 | request(app) 456 | .get('/pretty') 457 | .expect(302,done); 458 | } 459 | ); 460 | it('locals should be inherited', 461 | done => { 462 | let app = flicker() 463 | .add((req,res,next) => { 464 | res.locals = { user: 'me' }; 465 | next(); 466 | }) 467 | .add({ 468 | url: '/pretty', 469 | method: 'GET', 470 | handler: (req,res,next) => { 471 | res.send(res.locals.user); 472 | } 473 | }) 474 | request(app) 475 | .get('/pretty') 476 | .expect(200,'me',done); 477 | }); 478 | } 479 | ); 480 | 481 | describe('Request Object', 482 | () => { 483 | it('body should be inherited', 484 | done => { 485 | let app = flicker() 486 | let router = app.Router(); 487 | app.add(bodyParser.json()); 488 | router.add({ 489 | url: '/pretty', 490 | method: 'POST', 491 | handler: (req,res,next) => { 492 | res.send(req.body.name); 493 | } 494 | }) 495 | app.add(router); 496 | request(app) 497 | .post('/pretty') 498 | .send({ 499 | name: 'me' 500 | }) 501 | .expect(200,'me',done); 502 | }); 503 | 504 | it('cookies should be inherited', 505 | done => { 506 | let app = flicker() 507 | let router = app.Router(); 508 | app.add(cookieParser()) 509 | .add((req,res,next) => { 510 | req.cookies = {'foo':'bar'}; 511 | next(); 512 | }); 513 | router.add({ 514 | url: '/pretty', 515 | method:'GET', 516 | handler: (req,res,next) => { 517 | res.send(req.cookies.foo); 518 | } 519 | }) 520 | app.add(router); 521 | request(app) 522 | .get('/pretty') 523 | .expect(200,'bar',done); 524 | }); 525 | 526 | it('Url', 527 | done => { 528 | let app = flicker() 529 | let router = app.Router(); 530 | app.add(cookieParser()); 531 | router.add({ 532 | url: '/pretty', 533 | method: 'GET', 534 | handler: (req,res,next) => { 535 | res.send(req.url); 536 | } 537 | }) 538 | app.add(router); 539 | request(app) 540 | .get('/pretty?=df') 541 | .expect(200,'/pretty?=df',done); 542 | }); 543 | 544 | it('url case insensitive', 545 | done => { 546 | let app = flicker(); 547 | app 548 | .add({ 549 | url: '/foo', 550 | method: 'GET', 551 | handler: (req, res, next) => { 552 | res.send("Hello!"); 553 | } 554 | }) 555 | request(app) 556 | .get('/FOO') 557 | .expect(200,'Hello!',done); 558 | } 559 | ); 560 | it('Path', 561 | done => { 562 | let app = flicker() 563 | let router = app.Router(); 564 | app.add(cookieParser()); 565 | router.add({ 566 | url: '/pretty', 567 | method: 'GET', 568 | handler: (req,res,next) => { 569 | res.send(req.path); 570 | } 571 | }) 572 | app.add(router); 573 | request(app) 574 | .get('/pretty?=df') 575 | .expect(200,'/pretty',done); 576 | }); 577 | 578 | it('Method', 579 | done => { 580 | let app = flicker() 581 | let router = app.Router(); 582 | app.add(cookieParser()); 583 | router.add({ 584 | url: '/pretty', 585 | method: 'PUT', 586 | handler: (req,res,next) => { 587 | res.send(req.method); 588 | } 589 | }) 590 | app.add(router); 591 | request(app) 592 | .put('/pretty') 593 | .expect(200,'PUT',done); 594 | }); 595 | 596 | it('Params', 597 | done => { 598 | let app = flicker() 599 | let router = app.Router(); 600 | router.add({ 601 | url: '/user/:id', 602 | method: 'GET', 603 | handler: (req,res,next) => { 604 | res.send(req.params.id); 605 | } 606 | }) 607 | app.add(router); 608 | request(app) 609 | .get('/user/5') 610 | .expect(200,'5',done); 611 | } 612 | ); 613 | 614 | it('Query', 615 | done => { 616 | let app = flicker() 617 | .add((req,res,next) => { 618 | res.send(req.query()); 619 | }); 620 | request(app) 621 | .get('/?foo=bar') 622 | .expect(200,'foo=bar',done); 623 | } 624 | ); 625 | } 626 | ); 627 | 628 | describe('Serving Static content', 629 | () => { 630 | it('OK /favicon.ico', 631 | done => { 632 | let app = flicker() 633 | app.set('static dir',__dirname + '/public'); 634 | app.add(app.serveStatic()); 635 | request(app) 636 | .get('/favicon.ico') 637 | .expect(200,done); 638 | } 639 | ); 640 | 641 | it('OK /css/style.css', 642 | done => { 643 | let app = flicker() 644 | app.set('static dir',__dirname + '/public'); 645 | app.add(app.serveStatic()); 646 | request(app) 647 | .get('/css/style.css') 648 | .expect(200,done); 649 | } 650 | ); 651 | 652 | it('OK /js/index.js', 653 | done => { 654 | let app = flicker() 655 | app.set('static dir',__dirname + '/public'); 656 | app.add(app.serveStatic()); 657 | request(app) 658 | .get('/js/index.js') 659 | .expect(200,done); 660 | } 661 | ); 662 | 663 | it('OK /test.json', 664 | done => { 665 | let app = flicker() 666 | app.set('static dir',__dirname + '/public'); 667 | app.add(app.serveStatic()); 668 | request(app) 669 | .get('/test.json') 670 | .expect(200,done); 671 | } 672 | ); 673 | } 674 | ); 675 | -------------------------------------------------------------------------------- /test/public/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | box-sizing: border-box; 5 | font-family: "Helvetica",Arial,sans-serif; 6 | background-color: #E1DDD7; 7 | color: #009E60; 8 | font-size: 1.1rem; 9 | } 10 | h1 { 11 | padding: 50px 50px 0; 12 | font-size: 2.3rem; 13 | } 14 | 15 | p { 16 | padding: 0px 60px; 17 | } 18 | 19 | footer { 20 | padding: 60px; 21 | } 22 | 23 | span { 24 | font-size: 0.9rem; 25 | color: #818181; 26 | } 27 | pre { 28 | padding: 10px 60px; 29 | font-size: 1rem; 30 | color: #7B7B7B; 31 | } 32 | 33 | h2{ 34 | padding-left: 60px; 35 | } 36 | -------------------------------------------------------------------------------- /test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venturachrisdev/flickerjs/e8f4bde0a5bc8073cebfe78fdbd1e3b4d6689b7d/test/public/favicon.ico -------------------------------------------------------------------------------- /test/public/js/index.js: -------------------------------------------------------------------------------- 1 | var element = document.getElementById('flicker'); 2 | -------------------------------------------------------------------------------- /test/public/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "lorem": "ipsum" 4 | } 5 | -------------------------------------------------------------------------------- /test/views/index.pug: -------------------------------------------------------------------------------- 1 | h1= 'Hello World' 2 | --------------------------------------------------------------------------------