UNPKG

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