UNPKG

5.79 kBJavaScriptView Raw
1/* @flow */
2
3import type VueRouter from './index'
4import { resolvePath } from './util/path'
5import { assert, warn } from './util/warn'
6import { createRoute } from './util/route'
7import { fillParams } from './util/params'
8import { createRouteMap } from './create-route-map'
9import { normalizeLocation } from './util/location'
10
11export type Matcher = {
12 match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
13 addRoutes: (routes: Array<RouteConfig>) => void;
14};
15
16export function createMatcher (
17 routes: Array<RouteConfig>,
18 router: VueRouter
19): Matcher {
20 const { pathList, pathMap, nameMap } = createRouteMap(routes)
21
22 function addRoutes (routes) {
23 createRouteMap(routes, pathList, pathMap, nameMap)
24 }
25
26 function match (
27 raw: RawLocation,
28 currentRoute?: Route,
29 redirectedFrom?: Location
30 ): Route {
31 const location = normalizeLocation(raw, currentRoute, false, router)
32 const { name } = location
33
34 if (name) {
35 const record = nameMap[name]
36 if (process.env.NODE_ENV !== 'production') {
37 warn(record, `Route with name '${name}' does not exist`)
38 }
39 if (!record) return _createRoute(null, location)
40 const paramNames = record.regex.keys
41 .filter(key => !key.optional)
42 .map(key => key.name)
43
44 if (typeof location.params !== 'object') {
45 location.params = {}
46 }
47
48 if (currentRoute && typeof currentRoute.params === 'object') {
49 for (const key in currentRoute.params) {
50 if (!(key in location.params) && paramNames.indexOf(key) > -1) {
51 location.params[key] = currentRoute.params[key]
52 }
53 }
54 }
55
56 if (record) {
57 location.path = fillParams(record.path, location.params, `named route "${name}"`)
58 return _createRoute(record, location, redirectedFrom)
59 }
60 } else if (location.path) {
61 location.params = {}
62 for (let i = 0; i < pathList.length; i++) {
63 const path = pathList[i]
64 const record = pathMap[path]
65 if (matchRoute(record.regex, location.path, location.params)) {
66 return _createRoute(record, location, redirectedFrom)
67 }
68 }
69 }
70 // no match
71 return _createRoute(null, location)
72 }
73
74 function redirect (
75 record: RouteRecord,
76 location: Location
77 ): Route {
78 const originalRedirect = record.redirect
79 let redirect = typeof originalRedirect === 'function'
80 ? originalRedirect(createRoute(record, location, null, router))
81 : originalRedirect
82
83 if (typeof redirect === 'string') {
84 redirect = { path: redirect }
85 }
86
87 if (!redirect || typeof redirect !== 'object') {
88 if (process.env.NODE_ENV !== 'production') {
89 warn(
90 false, `invalid redirect option: ${JSON.stringify(redirect)}`
91 )
92 }
93 return _createRoute(null, location)
94 }
95
96 const re: Object = redirect
97 const { name, path } = re
98 let { query, hash, params } = location
99 query = re.hasOwnProperty('query') ? re.query : query
100 hash = re.hasOwnProperty('hash') ? re.hash : hash
101 params = re.hasOwnProperty('params') ? re.params : params
102
103 if (name) {
104 // resolved named direct
105 const targetRecord = nameMap[name]
106 if (process.env.NODE_ENV !== 'production') {
107 assert(targetRecord, `redirect failed: named route "${name}" not found.`)
108 }
109 return match({
110 _normalized: true,
111 name,
112 query,
113 hash,
114 params
115 }, undefined, location)
116 } else if (path) {
117 // 1. resolve relative redirect
118 const rawPath = resolveRecordPath(path, record)
119 // 2. resolve params
120 const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`)
121 // 3. rematch with existing query and hash
122 return match({
123 _normalized: true,
124 path: resolvedPath,
125 query,
126 hash
127 }, undefined, location)
128 } else {
129 if (process.env.NODE_ENV !== 'production') {
130 warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`)
131 }
132 return _createRoute(null, location)
133 }
134 }
135
136 function alias (
137 record: RouteRecord,
138 location: Location,
139 matchAs: string
140 ): Route {
141 const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`)
142 const aliasedMatch = match({
143 _normalized: true,
144 path: aliasedPath
145 })
146 if (aliasedMatch) {
147 const matched = aliasedMatch.matched
148 const aliasedRecord = matched[matched.length - 1]
149 location.params = aliasedMatch.params
150 return _createRoute(aliasedRecord, location)
151 }
152 return _createRoute(null, location)
153 }
154
155 function _createRoute (
156 record: ?RouteRecord,
157 location: Location,
158 redirectedFrom?: Location
159 ): Route {
160 if (record && record.redirect) {
161 return redirect(record, redirectedFrom || location)
162 }
163 if (record && record.matchAs) {
164 return alias(record, location, record.matchAs)
165 }
166 return createRoute(record, location, redirectedFrom, router)
167 }
168
169 return {
170 match,
171 addRoutes
172 }
173}
174
175function matchRoute (
176 regex: RouteRegExp,
177 path: string,
178 params: Object
179): boolean {
180 const m = path.match(regex)
181
182 if (!m) {
183 return false
184 } else if (!params) {
185 return true
186 }
187
188 for (let i = 1, len = m.length; i < len; ++i) {
189 const key = regex.keys[i - 1]
190 const val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i]
191 if (key) {
192 // Fix #1994: using * with props: true generates a param named 0
193 params[key.name || 'pathMatch'] = val
194 }
195 }
196
197 return true
198}
199
200function resolveRecordPath (path: string, record: RouteRecord): string {
201 return resolvePath(path, record.parent ? record.parent.path : '/', true)
202}