UNPKG

reblend-routing

Version:

Reblend route pattern matcher

221 lines (220 loc) 6.13 kB
/*! * 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;