├── .babelrc ├── .gitignore ├── package.json ├── readme.md └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | package-lock.json 3 | node_modules 4 | dist -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-route-middleware", 3 | "version": "1.0.7", 4 | "description": "Make complex route middleware logic look easy and readable!", 5 | "main": "dist/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/lionix-team/vue-route-middleware.git" 9 | }, 10 | "scripts": { 11 | "prepublishOnly": "babel ./src --out-dir ./dist -s inline" 12 | }, 13 | "keywords": [ 14 | "vue", 15 | "vuejs", 16 | "router", 17 | "routes", 18 | "vue-router", 19 | "middleware" 20 | ], 21 | "contributors": [ 22 | { 23 | "name": "Lionix Team", 24 | "email": "packages@lionix-team.com" 25 | }, 26 | { 27 | "name": "Stas Vartanyan", 28 | "email": "vaawebdev@gmail.com" 29 | } 30 | ], 31 | "license": "MIT", 32 | "homepage": "https://github.com/lionix-team/vue-route-middleware#readme", 33 | "dependencies": { 34 | "vue": "^2.6.10", 35 | "vue-router": "^3.1.3" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "^6.26.0", 39 | "babel-preset-es2015": "^6.24.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # vue-route-middleware  [![](https://img.shields.io/npm/dt/vue-route-middleware.svg)](https://www.npmjs.com/package/vue-route-middleware) [![](https://img.shields.io/npm/v/vue-route-middleware.svg)](https://www.npmjs.com/package/vue-route-middleware) 2 | 3 | 4 | Make complex global route middleware logic look easy and readable! 5 | 6 | 7 | ## Installation: 8 | 9 | ```bash 10 | npm i vue-route-middleware 11 | ``` 12 | 13 | 14 | ## Basic Usage: 15 | 16 | Set `middleware` meta key to your route and add our component to any vue router guard hook. 17 | 18 | ```js 19 | import VueRouteMiddleware from 'vue-route-middleware'; 20 | ... 21 | const Router = new VueRouter({ 22 | mode: 'history', 23 | routes: [{ 24 | path: '/dashboard', 25 | name: 'Dashboard', 26 | component: Dashboard, 27 | meta: { 28 | middleware: (to, from, next) => { 29 | let auth = fakeUser.isLogged(); 30 | if(!auth){ 31 | next({ name: 'Login' }); 32 | } 33 | } 34 | } 35 | }] 36 | }); 37 | ... 38 | Router.beforeEach(VueRouteMiddleware()); 39 | ``` 40 | 41 | 42 | **NOTICE:** Middleware function will retrieve all the variables normally passed to the router guards 43 | Example: (`to`, `from`, `next`) in case of `beforeEach` or (`to`, `from`) in case of `afterEach` guard. 44 | 45 | ### Chain middlewares with an array of functions: 46 | 47 | #### Example: 48 | 49 | ```js 50 | middleware: [ 51 | (to, from, next) => { 52 | let auth = fakeUser.isLogged(); 53 | if(!auth){ 54 | next({ name: 'Login' }); 55 | return false; // if false is returned, middleware chain will break! 56 | } 57 | }, 58 | (to, from, next) => { 59 | let hasPaied = fakeUser.madePayment(); 60 | if(!hasPaied){ 61 | next({ name: 'Payment' }); 62 | } 63 | } 64 | ] 65 | ``` 66 | 67 | 68 | **NOTICE:** If function returns `false` then the middleware chain will break and no longer execute. 69 | 70 | 71 | ### Separated middleware file example: 72 | 73 | #### ./route/middleware/auth.js 74 | 75 | ```js 76 | export default (to, from, next) => { 77 | let auth = fakeUser.isLogged(); 78 | if(!auth){ 79 | next({ name: 'Login' }); 80 | return false; 81 | } 82 | } 83 | ``` 84 | 85 | #### router.js 86 | 87 | ```js 88 | import AuthMiddleware from './route/middleware/auth'; 89 | ... 90 | meta: { 91 | middleware: [ AuthMiddleware ] 92 | } 93 | ... 94 | ``` 95 | 96 | 97 | ## Advanced: 98 | 99 | Another way to define middlewares is to pass them in the object as the first parameter to 100 | **VueRouteMiddleware** function and pass an array of middleware key names to the meta of the route. 101 | 102 | #### Example: 103 | 104 | ```js 105 | import AuthMiddleware from './route/middleware/auth'; 106 | import PaymentMiddleware from './route/middleware/payment'; 107 | ... 108 | meta: { 109 | middleware: [ 'AuthMiddleware', 'PaymentMiddleware' ] 110 | } 111 | ... 112 | Router.beforeEach(VueRouteMiddleware({ AuthMiddleware, PaymentMiddleware })); 113 | ``` 114 | 115 | This way we can differentiate middlewares that will be applied with different guards. 116 | For example you want to add tracking middleware to `afterEach` guard: 117 | 118 | #### Example: 119 | 120 | ```js 121 | import AuthMiddleware from './route/middleware/auth'; 122 | import PaymentMiddleware from './route/middleware/payment'; 123 | import TrackingMiddleware from './route/middleware/tracking'; 124 | ... 125 | meta: { 126 | // define all applied middleware to the route 127 | middleware: [ 'AuthMiddleware', 'PaymentMiddleware', 'TrackingMiddleware' ] 128 | } 129 | ... 130 | Router.beforeEach(VueRouteMiddleware({ AuthMiddleware, PaymentMiddleware })); 131 | // Pass the tracking function to afterEach guard 132 | Router.afterEach(VueRouteMiddleware({ TrackingMiddleware })); 133 | ``` 134 | 135 | At the example above `beforeEach` guard will apply chained `AuthMiddleware` and `PaymentMiddleware` ignoring the `TrackingMiddleware` that will be applied on `afterEach` guard. 136 | 137 | 138 | ## Credits: 139 | - [Lionix Team](https://github.com/lionix-team) 140 | - [Stas Vartanyan](https://github.com/vaawebdev) -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | class VueRouteMiddleware { 2 | 3 | /** 4 | * Set instance properties and call defined 5 | * middlewares on matching routes 6 | * 7 | * @param {object} definedMiddlewares 8 | * @param {object} to 9 | * @param {object} from 10 | * @param {function|undefined} next 11 | * 12 | * @var {object} middlewares // predefined middlewares 13 | * @var {boolean} nextHasCalled // if next was called in the middlewares 14 | * @var {array} toMiddleware // arguments passed to middleware function 15 | */ 16 | constructor(definedMiddlewares, to, from, next){ 17 | if(this._isObject(definedMiddlewares)){ 18 | this.middlewares = definedMiddlewares; 19 | } else { 20 | this._error('Defined middlewares must be of type Object!'); 21 | this.middlewares = {}; 22 | } 23 | this.to = to; 24 | this.from = from; 25 | this.next = next; 26 | this.nextHasCalled = false; 27 | if(this.to && this.to.matched){ // Apply middleware if anu route matched 28 | to.matched.every(route => this.applyRouteMiddlewares(route)); 29 | } 30 | if(this.next && !this.nextHasCalled){ // call next if user didnt call it 31 | this.callNext(); 32 | } 33 | } 34 | 35 | /** 36 | * Function used to pass arguments to middlewares with spred syntax 37 | * 38 | * @return {array} 39 | */ 40 | toMiddleware(){ 41 | return [ 42 | this.to, 43 | this.from, 44 | this._isFunction(this.next) ? 45 | this.callNext.bind(this) : undefined 46 | ]; 47 | } 48 | 49 | /** 50 | * Function that is passed to middleware as a next function wrapper 51 | * toggling `nextHasCalled` trigger 52 | * 53 | * @param {...any} args 54 | */ 55 | callNext(...args){ 56 | if(!this.nextHasCalled) this.nextHasCalled = true; 57 | return this.next(...args); 58 | }; 59 | 60 | /** 61 | * Fuction applying middlewares of a single route and deciding 62 | * if other matched routes should be checked as well 63 | * 64 | * @param {*} route 65 | * 66 | * @return {boolean} 67 | */ 68 | applyRouteMiddlewares(route){ 69 | if(route.meta && route.meta.middleware){ 70 | let middlewareKeys = route.meta.middleware; 71 | if(this._isArray(middlewareKeys)){ 72 | return middlewareKeys.every(middleware => this.applyMiddleware(middleware)); 73 | } else { 74 | return this.applyMiddleware(middlewareKeys); 75 | } 76 | } 77 | return true; 78 | } 79 | 80 | /** 81 | * Function calling middlewares and deciding if middleware chain 82 | * must be continued or stopped after first faliure 83 | * 84 | * @param {string|function} middleware 85 | * 86 | * @return {boolean} 87 | */ 88 | applyMiddleware(middleware){ 89 | const result = this.getMiddleware(middleware)(...this.toMiddleware()); 90 | return result === undefined ? true : result; 91 | } 92 | 93 | /** 94 | * Function to get middleware function. 95 | * In case of function validation failure 96 | * console the error and return empty function 97 | * 98 | * @param {string|function} middleware 99 | * 100 | * @return {function} 101 | */ 102 | getMiddleware(middleware){ 103 | if(this._isString(middleware)){ 104 | if(this.middlewares.hasOwnProperty(middleware)){ 105 | if(this._isFunction(this.middlewares[middleware])){ 106 | return this.middlewares[middleware]; 107 | } else { 108 | this._error(middleware+' is not a function!'); 109 | } 110 | } 111 | } else if(this._isFunction(middleware)) { 112 | return middleware; 113 | } else { 114 | this._error('All middlewares must be functions!'); 115 | } 116 | return () => true; 117 | } 118 | 119 | /** 120 | * @param {string} text 121 | * 122 | * @return {boolean} 123 | */ 124 | _error(text){ 125 | console.error(this.constructor.name + ': '+text); 126 | } 127 | 128 | /** 129 | * @param {*} toCheck 130 | * 131 | * @return {boolean} 132 | */ 133 | _isString(toCheck){ 134 | return typeof toCheck === "string" || toCheck instanceof String; 135 | } 136 | 137 | /** 138 | * @param {*} toCheck 139 | * 140 | * @return {boolean} 141 | */ 142 | _isArray(toCheck){ 143 | return Array.isArray(toCheck); 144 | } 145 | 146 | /** 147 | * @param {*} toCheck 148 | * 149 | * @return {boolean} 150 | */ 151 | _isFunction(toCheck){ 152 | return typeof toCheck === "function"; 153 | } 154 | 155 | /** 156 | * @param {*} toCheck 157 | * 158 | * @return {boolean} 159 | */ 160 | _isObject(toCheck){ 161 | return typeof toCheck === "object" && toCheck !== null; 162 | } 163 | }; 164 | 165 | export default (definedGroups = {}) => { 166 | return (...toMiddleware) => new VueRouteMiddleware(definedGroups, ...toMiddleware); 167 | }; --------------------------------------------------------------------------------