UNPKG

flask-router-plus

Version:

Flask-inspired routing system for node and connect. Nice if you just need a routing system without depending on connect, or need routing middleware without all features provided by express.

319 lines (305 loc) 10.4 kB
// Generated by CoffeeScript 1.6.3 (function() { var Compiler, Router, absoluteUrl, normalizePathname, send, url, __slice = [].slice, __hasProp = {}.hasOwnProperty; Compiler = require('./compiler'); url = require('url'); send = require('./send'); absoluteUrl = function(req, pathname, search) { var port, protocol, rv; protocol = 'http'; if (req.headers['x-protocol'] === 'https') { protocol = 'https'; } rv = [protocol, '://', req.headers.host]; if (port = req.headers['x-port']) { rv.push(":" + port); } rv.push(pathname); if (search) { rv.push(search); } return rv.join(''); }; normalizePathname = function(pathname) { var match, rv; rv = pathname.replace(/\/\.\//g, '/'); while (match = /\/[^/][^/]*\/\.\./.exec(rv)) { rv = rv.replace(match[0], ''); } rv = rv.replace(/\/\.\./g, ''); rv = rv.replace(/\.\//g, ''); return rv || '/'; }; Router = (function() { function Router(compiler) { this.compiler = compiler; this.rules = { GET: [], POST: [], PUT: [], DELETE: [], PATCH: [], OPTIONS: [], HEAD: [] }; this.compiled = false; } Router.prototype.route = function(req, res, next) { var checkRule, fail, matchedRules, p, ruleArray, urlObj, _this = this; if (typeof next !== 'function') { next = function(err) { var status; status = 404; if (err != null ? err.status : void 0) { status = err.status; } res.writeHead(status); return res.end(); }; } req.usePath = req.originalUrl.substr(0, req.originalUrl.length, -req.url.length); res.answer = function(code, headers, data) { var args; args = send["arguments"](code, headers, data); res.writeHead(args.code, args.headers); if (null !== args.data) { res.write(args.data); } return this.end(); }; urlObj = url.parse(req.url); p = normalizePathname(urlObj.pathname); req.path = p; this.compileRules(); ruleArray = this.rules[req.method]; matchedRules = {}; checkRule = function(idx, err) { var end, extracted, handle, handlerChain, rule, status; if (idx === ruleArray.length) { return fail(err); } rule = ruleArray[idx]; if (extracted = rule.extractor.extract(p)) { matchedRules[rule.id] = 1; req.params = extracted; end = res.end; status = { done: false }; res.end = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; status.done = true; return end.apply(res, args); }; handlerChain = rule.handlers; handle = function(i, err) { var current, n; n = function(arg) { if (status.done) { return; } if (arg === 'route') { return checkRule(idx + 1); } if (i === handlerChain.length - 1) { return checkRule(idx + 1, arg); } return handle(i + 1, arg); }; current = handlerChain[i]; if ((err && current.length < 4) || (!err && current.length > 3)) { return n(err); } else { return current(req, res, n, err); } }; return handle(0, err); } else { return checkRule(idx + 1, err); } }; fail = function(err) { var allowed, bp, extracted, method, rule, _i, _j, _len, _len1, _ref; bp = p + '/'; for (_i = 0, _len = ruleArray.length; _i < _len; _i++) { rule = ruleArray[_i]; if (extracted = rule.extractor.extract(bp)) { if (rule.id in matchedRules) { continue; } res.writeHead(301, { 'Location': absoluteUrl(req, bp, urlObj.search) }); res.end(); return; } } allowed = []; _ref = _this.rules; for (method in _ref) { if (!__hasProp.call(_ref, method)) continue; ruleArray = _ref[method]; if (method === req.method) { continue; } for (_j = 0, _len1 = ruleArray.length; _j < _len1; _j++) { rule = ruleArray[_j]; if (rule.extractor.test(p)) { if (rule.id in matchedRules) { continue; } allowed.push(method); break; } } } if (allowed.length) { res.writeHead(405, { 'Allow': allowed.join(', ') }); res.end(); return; } return next(err); }; return checkRule(0); }; Router.prototype.register = function() { var handler, handlerArray, handlers, id, methodName, pattern, prefix, rule, ruleArray, _i, _j, _len, _len1; prefix = arguments[0], methodName = arguments[1], pattern = arguments[2], handlers = 4 <= arguments.length ? __slice.call(arguments, 3) : []; ruleArray = this.rules[methodName]; if (this.compiled) { throw new Error('Cannot register rules after compilation'); } if (!(typeof pattern === 'string' || pattern instanceof RegExp)) { throw new Error('Pattern must be rule string or regex'); } id = "" + prefix + "#" + (pattern.toString()); handlerArray = null; for (_i = 0, _len = ruleArray.length; _i < _len; _i++) { rule = ruleArray[_i]; if (rule.id === id) { handlerArray = rule.handlers; break; } } if (!handlerArray) { handlerArray = []; ruleArray.push({ id: id, pattern: pattern, handlers: handlerArray }); } for (_j = 0, _len1 = handlers.length; _j < _len1; _j++) { handler = handlers[_j]; if (typeof handler === 'function') { handlerArray.push(handler); } else if (Array.isArray(handler)) { this.register.apply(this, [prefix, methodName, pattern].concat(handler)); } else { throw new Error('Handler must be a function or array of functions'); } } return handlers; }; Router.prototype.compileRules = function() { var compiled, method, rule, ruleArray, _i, _len, _ref; if (this.compiled) { return; } _ref = this.rules; for (method in _ref) { if (!__hasProp.call(_ref, method)) continue; ruleArray = _ref[method]; for (_i = 0, _len = ruleArray.length; _i < _len; _i++) { rule = ruleArray[_i]; rule.extractor = this.compiler.compile(rule.pattern); } } return compiled = true; }; return Router; })(); module.exports = function(parsers) { var compiler, r; if (!compiler) { compiler = new Compiler(parsers); } r = new Router(compiler); return { route: function(req, res, next) { return r.route(req, res, next); }, registerParser: function(name, parser) { return compiler.parsers[name] = parser; }, get: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['get', 'GET', pattern].concat(__slice.call(handlers))); }, options: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['options', 'OPTIONS', pattern].concat(__slice.call(handlers))); }, head: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['head', 'HEAD', pattern].concat(__slice.call(handlers))); }, post: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['post', 'POST', pattern].concat(__slice.call(handlers))); }, put: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['put', 'PUT', pattern].concat(__slice.call(handlers))); }, del: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['del', 'DELETE', pattern].concat(__slice.call(handlers))); }, patch: function() { var handlers, pattern; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return r.register.apply(r, ['patch', 'PATCH', pattern].concat(__slice.call(handlers))); }, all: function() { var handlers, method, pattern, _i, _len, _ref; pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; _ref = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { method = _ref[_i]; r.register.apply(r, ['all', method, pattern].concat(__slice.call(handlers))); } return handlers; }, use: function() { var handlers, usepath; usepath = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : []; if (!handlers.length) { handlers = [usepath]; usepath = '/'; } if (usepath.slice(-1) !== '/') { usepath += '/'; } handlers.unshift(function(req, res, next) { req.url = '/' + req.params.__path; return next(); }); this.all.apply(this, ["" + usepath + "<path:__path>"].concat(__slice.call(handlers))); return handlers; } }; }; }).call(this);