UNPKG

2.46 kBJavaScriptView Raw
1const http = require('http');
2const Router = require('trouter');
3const parseurl = require('parseurl');
4
5function strip(x) {
6 return x.charCodeAt(0) === 47 ? x.substring(1) : x;
7}
8
9function value(x) {
10 let y = x.indexOf('/', 1);
11 return y > 1 ? x.substring(1, y) : x.substring(1);
12}
13
14function onError(err, req, res, next) {
15 let code = (res.statusCode = err.code || err.status || 500);
16 res.end(err.length && err || err.message || http.STATUS_CODES[code]);
17}
18
19class Polka extends Router {
20 constructor(opts={}) {
21 super(opts);
22 this.apps = {};
23 this.wares = [];
24 this.bwares = {};
25 this.parse = parseurl;
26 this.handler = this.handler.bind(this);
27 this.server = http.createServer(this.handler);
28 this.onError = opts.onError || onError; // catch-all handler
29 this.onNoMatch = opts.onNoMatch || this.onError.bind(null, { code:404 });
30 }
31
32 use(base, ...fns) {
33 if (typeof base === 'function') {
34 this.wares = this.wares.concat(base, fns);
35 } else {
36 base = strip(base);
37 fns.forEach(fn => {
38 if (fn instanceof Polka) {
39 this.apps[base] = fn;
40 } else {
41 this.bwares[base] = (this.bwares[base] || []).concat(fn);
42 }
43 });
44 }
45 return this; // chainable
46 }
47
48 listen(port, hostname) {
49 return new Promise((res, rej) => {
50 this.server.listen(port, hostname, err => err ? rej(err) : res());
51 });
52 }
53
54 handler(req, res, info) {
55 info = info || this.parse(req);
56 let fn, arr=this.wares, obj=this.find(req.method, info.pathname);
57 if (obj) {
58 fn = obj.handler;
59 req.params = obj.params;
60 } else {
61 // Looking for sub-apps or extra middleware
62 let base = value(info.pathname);
63 req.originalUrl = req.url; // for somebody?
64 req.url = '/' + strip(req.url.substring(base.length + 1));
65 info.pathname = '/' + strip(info.pathname.substring(base.length + 1));
66 if (this.apps[base] !== void 0) {
67 fn = this.apps[base].handler.bind(null, req, res, info);
68 } else {
69 arr = arr.concat(this.bwares[base] || []);
70 fn = this.onNoMatch;
71 }
72 }
73 // Grab addl values from `info`
74 req.pathname = info.pathname;
75 req.search = info.search;
76 req.query = info.query;
77 // Exit if no middleware
78 let i=0, len=arr.length;
79 if (len === i) return fn(req, res);
80 // Otherwise loop thru all middlware
81 let next = err => err ? this.onError(err, req, res, next) : loop();
82 let loop = _ => res.finished || (i < len) ? arr[i++](req, res, next) : fn(req, res);
83 loop(); // init
84 }
85}
86
87module.exports = opts => new Polka(opts);