reblend-routing
Version:
Reblend route pattern matcher
221 lines (220 loc) • 6.13 kB
JavaScript
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* Copyright(c) 2024 Emmanuel Paul Elom
* MIT Licensed
*/
'use strict';
import Router from './router/Router';
import debugLib from 'debug';
import { compileQueryParser, methods } from './utils';
//@ts-ignore
import flatten from 'array-flatten';
//@ts-ignore
import setPrototypeOf from 'setprototypeof';
import Request from './Request';
const debug = debugLib('express:application');
const { slice } = Array.prototype;
class ReblendRouting {
parent;
cache = {};
settings = {};
locals = {};
request = null;
mountpath = '/';
_router = null;
constructor() {
this.init();
methods.forEach(method => {
this[method] = function (path) {
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
this.lazyrouter();
var route = this._router.route(path);
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
}
//@ts-ignore
get;
put;
post;
update;
option;
patch;
init() {
this.cache = {};
this.settings = {};
this.defaultConfiguration();
}
defaultConfiguration() {
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
debug('booting in %s mode', 'default');
// setup locals
this.locals = Object.create(null);
// top-most app is mounted at /
this.mountpath = '/';
// default locals
this.locals.settings = this.settings;
}
mount(parent) {
// inherit protos
setPrototypeOf(this.request, parent.request);
setPrototypeOf(this.settings, parent.settings);
}
lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing'),
});
}
}
handle(url, callback = () => { }) {
const req = new Request(url);
req.app = this;
this.request = req;
const router = this._router;
// no routes
if (!router) {
debug('no routes defined on app');
callback();
return;
}
router.handle(req, callback);
}
use(...args) {
let offset = 0;
let path = '/';
// default path to '/'
// disambiguate app.use([fn])
if (typeof args[0] !== 'function') {
let arg = args[0];
while (Array.isArray(arg) && arg.length !== 0) {
arg = arg[0];
}
// first arg is the path
if (typeof arg !== 'function') {
offset = 1;
path = args[0];
}
}
const fns = flatten(slice.call(args, offset));
if (fns.length === 0) {
throw new TypeError('app.use() requires a middleware function');
}
// setup router
this.lazyrouter();
const router = this._router;
fns.forEach((fn) => {
// non-express app
if (!fn || !fn.handle || !fn.set) {
return router.use(path, fn);
}
debug('.use app under %s', path);
fn.mountpath = path;
fn.parent = this;
// restore .app property on req and res
router.use(path, function mounted_app(req, next) {
fn.handle(req, (err) => {
next(err);
});
});
// mounted an app
this.mount(this);
}, this);
return this;
}
route(path) {
this.lazyrouter();
return this._router.route(path);
}
param(name, fn) {
this.lazyrouter();
if (Array.isArray(name)) {
for (let i = 0; i < name.length; i++) {
this.param(name[i], fn);
}
return this;
}
this._router.param(name, fn);
return this;
}
set(setting, val) {
if (arguments.length === 1) {
// app.get(setting)
return this.settings[setting];
}
debug('set "%s" to %o', setting, val);
// set value
this.settings[setting] = val;
// trigger matched settings
switch (setting) {
case 'query parser':
this.set('query parser fn', compileQueryParser(val));
break;
}
return this;
}
//@ts-ignore
get(setting) {
return this.settings[setting];
}
path() {
return this.parent ? this.parent.path() + this.mountpath : this.mountpath;
}
enabled(setting) {
return Boolean(this.set(setting));
}
disabled(setting) {
return !this.set(setting);
}
enable(setting) {
return this.set(setting, true);
}
disable(setting) {
return this.set(setting, false);
}
configure(env, fn) {
let envs = 'all';
const args = slice.call(arguments);
fn = args.pop();
if (args.length) {
envs = args;
}
if (envs === 'all' || envs.includes(this.settings.env)) {
fn?.call(this);
}
return this;
}
/**
* Log error using console.error.
*
* @param {Error} err
* @private
*/
logerror(err) {
/* istanbul ignore next */
//@ts-ignore
if (this.get('env') !== 'test')
console.error(err.stack || err.toString());
}
/**
* Convert a callback to a standard middleware function.
*
* @param {Function} fn
* @return {Function}
* @private
*/
wrap(fn) {
return ((...args) => fn.apply(this, args)).bind(this);
}
}
export default ReblendRouting;