This repository has been archived by the owner on Aug 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
144 lines (121 loc) · 3.86 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
'use strict'
// **Github:** https://github.com/toajs/toa-router
//
// **License:** MIT
const http = require('http')
const thunks = require('thunks')
const Trie = require('route-trie')
const thunk = thunks.thunk
const slice = Array.prototype.slice
const ROUTED = Symbol('Route')
class Router {
constructor (options) {
options = options || {}
if (typeof options === 'string') options = {root: options}
this.root = typeof options.root === 'string' ? options.root : '/'
if (!this.root.endsWith('/')) this.root += '/'
this._root = this.root.slice(0, -1)
this.trie = new Trie(options)
this.middleware = []
this._otherwise = null
}
define (pattern) {
return new Route(this, pattern)
}
otherwise (handler) {
let args = Array.isArray(handler) ? handler : slice.call(arguments)
this._otherwise = normalizeHandlers(args)
return this
}
use (fn) {
this.middleware.push(toThunkable(fn))
return this
}
toThunk () {
let router = this
return function (done) {
router.serve(this)(done)
}
}
serve (context) {
let router = this
return thunk.call(context, function * () {
let path = this.path
let method = this.method
let handlers = null
if (this[ROUTED] || (!path.startsWith(router.root) && path !== router._root)) return
this[ROUTED] = true
if (path === router._root) path = '/'
else if (router._root.length > 0) path = path.slice(router._root.length)
let matched = router.trie.match(path)
if (!matched.node) {
if (matched.tsr || matched.fpr) {
let url = matched.tsr
if (matched.fpr) url = matched.fpr
if (router.root.length > 1) {
url = router.root + url.slice(1)
}
this.path = url
this.status = method === 'GET' ? 301 : 307
return this.redirect(this.url)
}
if (!router._otherwise) {
this.throw(501, `"${this.path}" not implemented.`)
}
handlers = router._otherwise
} else {
handlers = matched.node.getHandler(method)
if (!handlers) {
// OPTIONS support
if (method === 'OPTIONS') {
this.status = 204
this.set('allow', matched.node.getAllow())
return this.end()
}
handlers = router._otherwise
if (!handlers) {
// If no route handler is returned, it's a 405 error
this.set('allow', matched.node.getAllow())
this.throw(405, `"${this.method}" is not allowed in "${this.path}"`)
}
}
}
this.params = this.request.params = matched.params
for (let fn of router.middleware) yield fn
for (let fn of handlers) yield fn
})
}
}
class Route {
constructor (router, pattern) {
this.node = router.trie.define(pattern)
}
}
for (let method of http.METHODS) {
let _method = method.toLowerCase()
Router.prototype[_method] = function (pattern) {
let args = Array.isArray(arguments[1]) ? arguments[1] : slice.call(arguments, 1)
this.trie.define(pattern).handle(method, normalizeHandlers(args))
return this
}
Route.prototype[_method] = function (handler) {
let args = Array.isArray(handler) ? handler : slice.call(arguments)
this.node.handle(method, normalizeHandlers(args))
return this
}
}
Router.prototype.del = Router.prototype.delete
Route.prototype.del = Route.prototype.delete
function normalizeHandlers (handlers) {
if (!handlers.length) throw new Error('No router handler')
return handlers.map(toThunkable)
}
function toThunkable (fn) {
if (typeof fn === 'function') {
if (thunks.isThunkableFn(fn)) return fn
return function (done) { thunk.call(this, fn.call(this))(done) }
}
if (fn && typeof fn.toThunk === 'function') return fn
throw new TypeError(`${fn} is not a function or thunkable object!`)
}
module.exports = Router