light-router
Version:
A node.js perfomance efficient http router.
247 lines (197 loc) • 6.63 kB
JavaScript
//Dependencies
var Routes = require('./routes')
//Parse a route and put it into the routing table
function Parser(pathname, handler) {
//Narrow it down with the method table
var method = this.method
var table = Routes[method]
//Check if returning function was passed
if(typeof handler !== 'function') {
throw 'No handler function was passed'
}
//Remove the first and last slash frm the expression if present
pathname = trimPathname(pathname)
//Split the path into paths to start indexing
var paths = splitPath(pathname)
//Size, this will be used as an index later on
var size = paths.length
//Check if its a static route
var params = pathname2ParamsArray(paths)
//Its static, add to static routes and finish
if(params.length === 0) {
table.staticRoutes[pathname] = {
handler: handler
}
table = table.staticRoutes[pathname] //recurse into
}
//Else, its a dynamic route, recurse into dynamicRoutes
else {
table = table.dynamicRoutes
//Check if a routing table for the given size filter already exists, if not create
if(typeof table[size] === 'undefined') {
table[size] = {
params: [],
bases: {},
}
}
//Recurse into table filtered by size
table = table[size]
//Add route to dynamic table table
for(var i = 0; i < size; i++) {
var path = paths[i]
var param_index = indexOfParam(i, params)
//Its an parameter
if(param_index > -1) {
var param = params[ param_index ]
var length = table.params.push({
param: param.name,
})
//Recurse into table created
table = table.params[ length - 1 ]
//Check if a regexp was passed
if(typeof param.regexp !== 'undefined') {
table.regexp = new RegExp(param.regexp)
}
//Add routing scheme
table.routes = {
params: [],
bases: {}
}
//Check if this is the last iteration, if so, add the handler
if(i >= size - 1) {
table = table.routes //recurse into
table.handler = handler
}
//Else recurse into next table we will be working in
else {
table = table.routes
}
}
//Its a base path
else {
//Create a new table, if not exists
if(typeof table.bases[path] === 'undefined') {
table.bases[path] = {
params: [],
bases: {},
}
}
//Recurse into routing table
table = table.bases[path]
//Check if this is the last iteration, if so, add the handler
if(i >= size - 1) {
table.handler = handler
}
}
}
}
//Pointer to final table
//The final table contains all metadata of the route
this.table = table
return this
}
//Helper function to trim the pathnames first and last slash
//@return string (trimed pathname)
function trimPathname(pathname) {
if(pathname.charAt(0) === '/') {
pathname = pathname.substring(1)
}
if(pathname.charAt(pathname.length - 1) === '/') {
pathname = pathname.substring(pathname.length)
}
return pathname
}
//Helper function to construct an array with parameters of a pathname
//@return [] (parameters found)
function pathname2ParamsArray(paths) {
var params = []
for(var p in paths) {
var path = paths[p]
if(path.charAt(0) === ':') {
var push_data = {
pos: parseInt(p)
}
//Remove `:` from path string
path = path.substring(1)
//Check if an regexp was passed
if(path.indexOf('(') > -1 && path.lastIndexOf(')') > -1) {
//Start, end
var start = path.indexOf('(')
var end = path.lastIndexOf(')')
//Extract regexp string from path
var regexp = path.substring(start + 1, end)
//Remove regexp from path identifier
path = path.substring(0, start)
push_data.regexp = regexp
}
//Append parameter identifier
push_data.name = path
params.push(push_data)
}
}
return params;
}
//Helper function to retrieve parameters inside an given array of paths
//@return integer (index in the array)
function indexOfParam(pos, parameters) {
for(var i in parameters) {
if(parameters[i].pos == pos) {
return i
}
}
return -1
}
//Helper function to properly parse a url, this replaces require('url').parse(path).pathname
function urlParse(url) {
var query = url.indexOf('?')
if(query !== -1) {
url = url.substring(0, query)
}
return url
}
//Helper function to split the path into smaller base paths
//A simple pathname.split('/') wont work, since regexp can contain slashes
//@input string
//@return array
function splitPath(path) {
var paths = []
var buffer = ''
var i = 0
//Modes
var inParameter = false, inRegexp = false
while(i < path.length) {
//Check if a parameter is starting
if(path[i - 1] == '/' && path[i] == ':') {
inParameter = true
}
//Check if they passed a regexp
//This initialize the regexp buffer
if(inParameter === true && inRegexp === false && path[i] == '(') {
inRegexp = true
}
//Deactivate
else if(inRegexp === true && path[i] == ')' && path[i + 1] == '/') {
inRegexp = false
}
//Clear buffer or append
if(path[i] == '/' && inRegexp === false) {
//Clear buffer
paths.push(buffer)
buffer = ''
inParameter = false //default
}
else {
buffer = buffer + path[i]
}
i++ //next character
}
//Push any remaining buffers
if(buffer) {
paths.push(buffer)
}
return paths
}
//Exports
Parser.trimPathname = trimPathname
Parser.url = urlParse
module.exports = Parser