Last active
January 6, 2024 04:17
-
-
Save unbug/dd596d79b5eace7d245f0a4db6cd2be5 to your computer and use it in GitHub Desktop.
Powerful Javascript Middleware Pattern Implementation, apply middleweares to any object. https://unbug.github.io/js-middleware/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 'use strict'; | |
| /** | |
| * Composes single-argument functions from right to left. The rightmost | |
| * function can take multiple arguments as it provides the signature for | |
| * the resulting composite function. | |
| * | |
| * @param {...Function} funcs The functions to compose. | |
| * @returns {Function} A function obtained by composing the argument functions | |
| * from right to left. For example, compose(f, g, h) is identical to doing | |
| * (...args) => f(g(h(...args))). | |
| */ | |
| export function compose(...funcs) { | |
| if (funcs.length === 0) { | |
| return arg => arg; | |
| } | |
| funcs = funcs.filter(func => typeof func === 'function'); | |
| if (funcs.length === 1) { | |
| return funcs[0]; | |
| } | |
| const last = funcs[funcs.length - 1]; | |
| const rest = funcs.slice(0, -1); | |
| return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)); | |
| } | |
| let targetsCache = []; | |
| /** | |
| * Apply middleweare to an object. | |
| * Middleware functions are functions that have access to the target function and it's arguments, | |
| * and the target object and the next middleware function in the target function cycle. | |
| * The next middleware function is commonly denoted by a variable named next. | |
| * | |
| * Middleware functions can perform the following tasks: | |
| * - Execute any code. | |
| * - Make changes to the function's arguments. | |
| * - End the target function. | |
| * - Call the next middleware in the stack. | |
| * | |
| * If the current middleware function does not end the target function cycle, | |
| * it must call next() to pass control to the next middleware function. Otherwise, | |
| * the target function will be left hanging. | |
| * | |
| * e.g. | |
| * ``` | |
| * const walk = target => next => (...args) => { | |
| * this.log(`walk function start.`); | |
| * const result = next(...args); | |
| * this.log(`walk function end.`); | |
| * return result; | |
| * } | |
| * ``` | |
| * | |
| * Middleware object is an object that contains function's name as same as the target object's function name. | |
| * | |
| * e.g. | |
| * ``` | |
| * const Logger = { | |
| * walk: target => next => (...args) => { | |
| * console.log(`walk function start.`); | |
| * const result = next(...args); | |
| * console.log(`walk function end.`); | |
| * return result; | |
| * } | |
| * } | |
| * ``` | |
| * | |
| * Function's name start or end with "_" will not be able to apply middleware. | |
| * | |
| * @example | |
| * | |
| * // the target object | |
| * class Person { | |
| * // the target function | |
| * walk(step) { | |
| * this.step = step; | |
| * } | |
| * } | |
| * | |
| * // middleware for walk function | |
| * const logger = target => next => (...args) => { | |
| * this.log(`walk start, steps: ${args[0]}.`); | |
| * const result = next(...args); | |
| * this.log(`walk end.`); | |
| * return result; | |
| * } | |
| * | |
| * // apply middleware to target object | |
| * const p = new Person(); | |
| * const applyMiddleware = new ApplyMiddleware(p); | |
| * applyMiddleware.use('walk', walk); | |
| * p.walk(); | |
| * | |
| */ | |
| export class ApplyMiddleware { | |
| /** | |
| * @param {object} target The target object. | |
| * @param {...object} middlewareObjects Middleware objects. | |
| * @return {object} this | |
| */ | |
| constructor(target, ...middlewareObjects) { | |
| let instance = targetsCache.find(function (key) { | |
| return key === target; | |
| }) || this; | |
| instance._target = instance._target || target; | |
| instance._methods = instance._methods || {}; | |
| instance._methodMiddlewares = instance._methodMiddlewares || {}; | |
| instance.use(...middlewareObjects); | |
| return instance; | |
| } | |
| _applyToMethod(methodName, ...middlewares) { | |
| if (typeof methodName === 'string' && !/^_+|_+$/g.test(methodName)) { | |
| let method = this._methods[methodName] || this._target[methodName]; | |
| if (typeof method === 'function') { | |
| this._methods[methodName] = method; | |
| if (this._methodMiddlewares[methodName] === undefined) { | |
| this._methodMiddlewares[methodName] = []; | |
| } | |
| middlewares.forEach(middleware => | |
| typeof middleware === 'function' && this._methodMiddlewares[methodName].push(middleware(this._target)) | |
| ); | |
| this._target[methodName] = compose(...this._methodMiddlewares[methodName])(method.bind(this._target)); | |
| } | |
| } | |
| } | |
| /** | |
| * Apply middleware to the target object. | |
| * @param {string|object} methodName String for target function name, object | |
| * @param {...function} middlewares The middleware chain to be applied. | |
| * @return {object} this | |
| */ | |
| use(methodName, ...middlewares) { | |
| if (typeof methodName === 'object') { | |
| Array.prototype.slice.call(arguments).forEach(arg => { | |
| // A middleweare object can specify targer functions within middlewareMethods (Array). | |
| // e.g. obj.middlewareMethods = ['method1', 'method2']; | |
| // only method1 and method2 will be the target function. | |
| typeof arg === 'object' && (arg.middlewareMethods || Object.keys(arg)).forEach(key => { | |
| this._applyToMethod(key, arg[key].bind(arg)); | |
| }); | |
| }); | |
| } else { | |
| this._applyToMethod(methodName, ...middlewares); | |
| } | |
| return this; | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
js-middleware
Powerful Javascript Middleware Pattern implementation, apply middleweares to any object.
A painless solution to make codes as scalable and maintainable as ReduxJS and ExpressJS.
Links
Overview
Middleware functions are functions that have access to the target function and it's arguments,
and the target object and the next middleware function in the target function cycle.
The next middleware function is commonly denoted by a variable named next.
Middleware functions can perform the following tasks:
If the current middleware function does not end the target function cycle,
it must call next() to pass control to the next middleware function. Otherwise,
the target function will be left hanging.
Get started
dist/middleware.min.jsfile in your HTML.and import it in your files