Skip to content

Commit 23eebd0

Browse files
committed
feat(global-middlewares): register global middlewares in plugin options
2 parents f9e910b + c5c9dde commit 23eebd0

16 files changed

+308
-71
lines changed

.editorconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
[*.{js,jsx,ts,tsx}]
1+
[*.{js,jsx,ts,tsx,json,yml}]
22
indent_style = space
33
indent_size = 2
44
trim_trailing_whitespace = true
55
insert_final_newline = true
6+
end_of_line = lf

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cache:
55
- node_modules
66
branches:
77
only:
8-
- master # trigger release only for PRs and changes pushed to master branch
8+
- master
99
notifications:
1010
email: false
1111
node_js:
@@ -15,7 +15,7 @@ before_install:
1515
before_script:
1616
- npm prune
1717
script:
18-
- npm run test
1918
- npm run build
2019
after_success:
21-
- npm run semantic-release
20+
- bash <(curl -s https://codecov.io/bash)
21+
- npm run semantic-release

README.md

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# vue-router-middleware-plugin
22

3-
[![npm version](https://badge.fury.io/js/vue-router-middleware-plugin.svg)](https://badge.fury.io/js/vue-router-middleware-plugin)
4-
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d1ab723bcfaa460aa9d12ccc7a54bf65)](https://www.codacy.com/manual/dsfx3d/vue-router-middleware-plugin?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=dsfx3d/vue-router-middleware-plugin&amp;utm_campaign=Badge_Grade)
53
[![Build Status](https://travis-ci.org/dsfx3d/vue-router-middleware-plugin.svg?branch=master)](https://travis-ci.org/dsfx3d/vue-router-middleware-plugin)
4+
[![npm version](https://badge.fury.io/js/vue-router-middleware-plugin.svg)](https://badge.fury.io/js/vue-router-middleware-plugin)
5+
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d1ab723bcfaa460aa9d12ccc7a54bf65)](https://www.codacy.com/manual/dsfx3d/vue-router-middleware-plugin?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=dsfx3d/vue-router-middleware-plugin&amp;utm_campaign=Badge_Grade)
6+
![vue-router-middleware-plugin](https://badgen.net/bundlephobia/min/vue-router-middleware-plugin)
7+
![vue-router-middleware-plugin](https://badgen.net/bundlephobia/minzip/vue-router-middleware-plugin)
68

7-
> **Please Note:** Due to the very limited scope of this module, I do not anticipate needing to make very many changes to it. Expect long stretches of zero updates—that does not mean that the module is outdated.
9+
> **Please Note:** Due to the very limited scope of this module, I do not anticipate need to making many changes to it. Expect long stretches of zero updates—that does not mean that the module is outdated.
810
9-
A vue.js plugin to implement a middleware pipeline between your routes.
11+
A vue.js plugin to implement a middleware pipeline for vue-router.
1012

11-
It can be utilized for many use cases like protecting a route or to request an API to populate the store before a route is loaded.
13+
It can have many use cases like protecting a route or to request an API to populate the store before a route is loaded.
1214
The plugin utilizes [vue-router navigation guards](https://router.vuejs.org/guide/advanced/navigation-guards.html) to implement easy to use, readable and more organized middlewares for your routes.
1315

1416
## Installation
@@ -27,70 +29,140 @@ yarn add vue-router-middleware-plugin
2729

2830
## Usage
2931

32+
Create a middleware, `auth.js`
33+
34+
```javascript
35+
import store from '~/store'
36+
37+
export default ({ to, from, next }) => {
38+
if (store.getters.isLoggedIn) {
39+
next()
40+
} else {
41+
next('/login')
42+
}
43+
}
44+
```
45+
3046
Register the plugin in your application.
3147

3248
```javascript
3349
import Vue from 'vue'
3450
import MiddlewarePlugin from 'vue-router-middleware-plugin'
35-
/* vue-router is required */
3651
import router from '~/router'
52+
import AuthMiddleware from '~router/middlewares/auth'
53+
import LoggerMiddleware from '~router/middlewares/logger'
3754

3855
Vue.use(MiddlewarePlugin, router)
56+
57+
// OR register a globabl middleware
58+
59+
Vue.use(MiddlewarePlugin, { router, middleware: AuthMiddleware })
60+
61+
// OR register multiple globabl middlewares
62+
63+
Vue.use(MiddlewarePlugin, { router, middleware: [AuthMiddleware, LoggerMiddleware] })
3964
```
4065

41-
Create a middleware, `auth-example.js`
66+
> **Note:**: As the name suggests a global middleware will be resolved before each route.
67+
68+
Attach route middlewares in `router.js`
4269

4370
```javascript
44-
import store from '~/store'
71+
import ExampleMiddleware from './middlewares/example'
72+
import AnotherMiddleware from './middlewares/another'
4573

46-
export default ({ to, from, next }) => {
47-
if (store.getters.isLoggedIn) {
48-
next()
49-
} else {
50-
next('/login')
51-
}
52-
}
74+
.
75+
.
76+
.
77+
const routes = [
78+
{
79+
path: '/dashboard',
80+
component: DashboardView,
81+
meta: {
82+
// attavh a middleware to a route
83+
middleware: ExampleMiddleware
84+
}
85+
},
86+
{
87+
path: '/profile',
88+
component: ProfileView,
89+
meta: {
90+
// you may also use an array for multiple middlewares
91+
middleware: [ExampleMiddleware, AnotherMiddleware]
92+
}
93+
},
94+
.
95+
.
96+
.
97+
]
98+
```
99+
100+
> **Note**: Global middlewares have precedence over route middlewares
101+
102+
If the user tries to access `/profile` route, the attached middlewares will be resolved in a synchronous order:
103+
104+
```bash
105+
AuthMiddleware > LoggerMiddleware > ExampleMiddleware > AnotherMiddleware
53106
```
54107

55-
Attach the middleware to the routes in `router.js`
108+
`AuthMiddleware` and `LoggerMiddleware` will be resolved before profile route middlewares because they are global middlewares.
109+
110+
Ignore global middlewares in `router.js`
56111

57112
```javascript
58-
import AuthExampleMiddleware from './middlewares/auth-example'
113+
import AuthMiddleware from './middlewares/auth'
114+
import LoggerMiddleware from './middlewares/logger'
115+
import ExampleMiddleware from './middlewares/example'
59116
import AnotherMiddleware from './middlewares/another'
60117

61118
.
62119
.
63120
.
64121
const routes = [
122+
.
123+
.
124+
.
65125
{
66126
path: '/login',
67-
component: LoginView
127+
component: LoginView,
128+
meta: {
129+
middleware: {
130+
ignore: AuthMiddleware
131+
}
132+
}
68133
},
69134
{
70-
path: '/dashboard',
71-
component: DashboardView,
135+
path: '/register',
136+
component: RegisterView,
72137
meta: {
73-
/* Attach a middleware to a route */
74-
middleware: AuthExampleMiddleware
138+
// you may also attach a middleware to this route
139+
middleware: {
140+
ignore: AuthMiddleware
141+
attach: AnotherMiddleware
142+
}
75143
}
76144
},
145+
77146
{
78-
path: '/profile',
79-
component: ProfileView,
80-
/* You can also attach multiple middlewares to a route */
147+
path: '/about',
148+
component: AboutView,
81149
meta: {
82-
middleware: [AuthExampleMiddleware, AnotherMiddleware]
150+
// you may also use an array for multiple middlewares
151+
middleware: {
152+
ignore: [AuthMiddleware, LoggerMiddleware]
153+
attach: [ExampleMiddleware, AnotherMiddleware]
154+
}
83155
}
84156
}
85157
]
86158

87159
```
88160

89-
If the user tries to access `/profile` route, the attached middlewares will be resolved in a synchronous order. i.e. `AuthExampleMiddleware` will be resolved first and `AnotherMiddleware` afterwards.
90-
91161
## Roadmap
92162

93-
* **v1.1.0** - Global Middlewares
163+
- [x] **v1.0.0** - Route Middlewares
164+
- [x] **v1.1.0** - Global Middlewares
165+
- [ ] **v1.2.0** - Auto importing middlewares.
94166

95167
## Contributing
96168

jest.config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
'coverageThreshold': {
3+
'global': {
4+
'branches': 100,
5+
'functions': 100,
6+
'lines': 100,
7+
'statements': 100
8+
}
9+
}
10+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"scripts": {
1111
"type-check": "tslint-config-prettier-check ./tslint.json",
1212
"type-check:watch": "tsc --noEmit --watch",
13+
"prebuild": "npm run precommit",
1314
"build": "npm run build:types && npm run build:js",
1415
"build:types": "tsc --emitDeclarationOnly",
1516
"build:js": "babel src --out-dir build --extensions \".ts,.tsx\" --source-maps inline",

src/helpers/middlewarePipeline.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
import { Middleware, MiddlewarePipeline } from '../types/MiddlewareTypes'
1+
import { InvalidPipelinePayload } from '../lib/Exceptions/InvalidPipelinePayloads'
2+
import { Middleware } from '../types/MiddlewareTypes'
23
import { RouteContext, RouteResolver } from '../types/VueTypes'
34

4-
export const middlewarePipeline: MiddlewarePipeline = (
5+
export const middlewarePipeline = (
56
context: RouteContext,
6-
middleware: Middleware[] | Middleware,
7+
middlewares: Middleware[],
78
index = 0
89
) => {
9-
if (Array.isArray(middleware) && index === middleware.length) {
10+
if (!Array.isArray(middlewares)) {
11+
throw new InvalidPipelinePayload()
12+
}
13+
14+
if (index === middlewares.length) {
1015
return context.next
1116
}
1217

13-
let nextContext: RouteContext
14-
// tslint:disable-next-line: variable-name
15-
let thisMiddleware: Middleware
18+
const thisMiddleware = middlewares[index]
19+
const nextMiddleware = middlewarePipeline(context, middlewares, index + 1)
20+
const thisContext = { ...context, next: nextMiddleware as RouteResolver }
1621

17-
if (Array.isArray(middleware)) {
18-
thisMiddleware = middleware[index]
19-
const nextMiddleware = middlewarePipeline(context, middleware, index + 1)
20-
nextContext = { ...context, next: nextMiddleware as RouteResolver }
21-
} else {
22-
thisMiddleware = middleware
23-
nextContext = context
24-
}
25-
const nextResolver = () => thisMiddleware(nextContext)
22+
const nextResolver = () => thisMiddleware(thisContext)
2623
return nextResolver
2724
}

src/helpers/returnMiddlewareArray.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { NotAMiddleware } from '../lib/Exceptions/NotAMiddleware'
2+
3+
export const retuenMiddlewareArray = (
4+
x: any | any[],
5+
arr: any[] = []
6+
): any[] => {
7+
if (!Array.isArray(arr)) {
8+
throw new NotAMiddleware()
9+
} else {
10+
const allMiddlewares = (arr as []).every(a => typeof a === 'function')
11+
if (!allMiddlewares) {
12+
throw new NotAMiddleware()
13+
}
14+
}
15+
16+
if (Array.isArray(x) && x.length) {
17+
const allMiddlewares = x.every(_x => typeof _x === 'function')
18+
if (allMiddlewares) {
19+
return [...arr, ...x]
20+
}
21+
throw new NotAMiddleware()
22+
}
23+
24+
if (typeof x === 'function') {
25+
arr.push(x)
26+
} else {
27+
throw new NotAMiddleware()
28+
}
29+
30+
return arr
31+
}

src/install.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { middlewarePipeline } from './helpers/middlewarePipeline'
2+
import { retuenMiddlewareArray } from './helpers/returnMiddlewareArray'
23
import { OptionsMissingPluginError } from './lib/Exceptions/OptionsMissingPluginError'
4+
import { Middleware } from './types/MiddlewareTypes'
35
import { Install, PluginOptions } from './types/PluginTypes'
46
import {
57
Route,
@@ -14,10 +16,20 @@ export const install: Install<Router | PluginOptions> = (
1416
vue: Vue,
1517
options?: Router | PluginOptions
1618
) => {
17-
const router: Router =
18-
options && (options as PluginOptions).router
19-
? (options as PluginOptions).router
20-
: (options as Router)
19+
let router: Router
20+
let middlewares: Middleware[] = []
21+
22+
if (options && (options as PluginOptions).router) {
23+
const { router: _router, middleware } = options as PluginOptions
24+
router = _router
25+
26+
/* istanbul ignore if */
27+
if (middleware !== undefined) {
28+
middlewares = retuenMiddlewareArray(middleware)
29+
}
30+
} else {
31+
router = options as Router
32+
}
2133

2234
/* istanbul ignore else */
2335
if (router === undefined) {
@@ -28,15 +40,35 @@ export const install: Install<Router | PluginOptions> = (
2840
from: Route,
2941
next: RouteResolver
3042
) => {
31-
if (!to.meta.middleware) {
32-
next()
33-
} else {
43+
if ('middleware' in to.meta) {
44+
if (typeof to.meta.middleware === 'object') {
45+
let ignores: Middleware[] = []
46+
if ('attach' in to.meta.middleware) {
47+
middlewares = retuenMiddlewareArray(
48+
to.meta.middleware.attach,
49+
middlewares
50+
)
51+
}
52+
if ('ignore' in to.meta.middleware) {
53+
ignores = retuenMiddlewareArray(to.meta.middleware.ignore)
54+
}
55+
56+
middlewares = middlewares.filter(
57+
middleware => !ignores.includes(middleware)
58+
)
59+
} else {
60+
middlewares = retuenMiddlewareArray(to.meta.middleware, middlewares)
61+
}
62+
}
63+
if (middlewares.length) {
3464
const context: RouteContext = { to, from, next }
3565
const routeResolver = middlewarePipeline(
3666
context,
37-
to.meta.middleware
67+
middlewares
3868
) as RouteResolver
3969
routeResolver()
70+
} else {
71+
next()
4072
}
4173
}
4274

src/lib/Exceptions/BasePluginError.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
export class BasePluginError extends Error {
44
protected static readonly _MESSAGE_: string = 'unexpected error'
5-
protected static readonly _NAME_: string = '[better-vue-router-middleware]'
5+
protected static readonly _NAME_: string = '[vue-router-middleware-plugin]'
66

77
constructor(message: string = BasePluginError._MESSAGE_) {
88
super(message)

0 commit comments

Comments
 (0)