UNPKG

4.85 kBJavaScriptView Raw
1/* @flow */
2
3import Regexp from 'path-to-regexp'
4import { cleanPath } from './util/path'
5import { assert, warn } from './util/warn'
6
7export function createRouteMap (
8 routes: Array<RouteConfig>,
9 oldPathList?: Array<string>,
10 oldPathMap?: Dictionary<RouteRecord>,
11 oldNameMap?: Dictionary<RouteRecord>
12): {
13 pathList: Array<string>;
14 pathMap: Dictionary<RouteRecord>;
15 nameMap: Dictionary<RouteRecord>;
16} {
17 // the path list is used to control path matching priority
18 const pathList: Array<string> = oldPathList || []
19 // $flow-disable-line
20 const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)
21 // $flow-disable-line
22 const nameMap: Dictionary<RouteRecord> = oldNameMap || Object.create(null)
23
24 routes.forEach(route => {
25 addRouteRecord(pathList, pathMap, nameMap, route)
26 })
27
28 // ensure wildcard routes are always at the end
29 for (let i = 0, l = pathList.length; i < l; i++) {
30 if (pathList[i] === '*') {
31 pathList.push(pathList.splice(i, 1)[0])
32 l--
33 i--
34 }
35 }
36
37 return {
38 pathList,
39 pathMap,
40 nameMap
41 }
42}
43
44function addRouteRecord (
45 pathList: Array<string>,
46 pathMap: Dictionary<RouteRecord>,
47 nameMap: Dictionary<RouteRecord>,
48 route: RouteConfig,
49 parent?: RouteRecord,
50 matchAs?: string
51) {
52 const { path, name } = route
53 if (process.env.NODE_ENV !== 'production') {
54 assert(path != null, `"path" is required in a route configuration.`)
55 assert(
56 typeof route.component !== 'string',
57 `route config "component" for path: ${String(path || name)} cannot be a ` +
58 `string id. Use an actual component instead.`
59 )
60 }
61
62 const pathToRegexpOptions: PathToRegexpOptions = route.pathToRegexpOptions || {}
63 const normalizedPath = normalizePath(
64 path,
65 parent,
66 pathToRegexpOptions.strict
67 )
68
69 if (typeof route.caseSensitive === 'boolean') {
70 pathToRegexpOptions.sensitive = route.caseSensitive
71 }
72
73 const record: RouteRecord = {
74 path: normalizedPath,
75 regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
76 components: route.components || { default: route.component },
77 instances: {},
78 name,
79 parent,
80 matchAs,
81 redirect: route.redirect,
82 beforeEnter: route.beforeEnter,
83 meta: route.meta || {},
84 props: route.props == null
85 ? {}
86 : route.components
87 ? route.props
88 : { default: route.props }
89 }
90
91 if (route.children) {
92 // Warn if route is named, does not redirect and has a default child route.
93 // If users navigate to this route by name, the default child will
94 // not be rendered (GH Issue #629)
95 if (process.env.NODE_ENV !== 'production') {
96 if (route.name && !route.redirect && route.children.some(child => /^\/?$/.test(child.path))) {
97 warn(
98 false,
99 `Named Route '${route.name}' has a default child route. ` +
100 `When navigating to this named route (:to="{name: '${route.name}'"), ` +
101 `the default child route will not be rendered. Remove the name from ` +
102 `this route and use the name of the default child route for named ` +
103 `links instead.`
104 )
105 }
106 }
107 route.children.forEach(child => {
108 const childMatchAs = matchAs
109 ? cleanPath(`${matchAs}/${child.path}`)
110 : undefined
111 addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
112 })
113 }
114
115 if (route.alias !== undefined) {
116 const aliases = Array.isArray(route.alias)
117 ? route.alias
118 : [route.alias]
119
120 aliases.forEach(alias => {
121 const aliasRoute = {
122 path: alias,
123 children: route.children
124 }
125 addRouteRecord(
126 pathList,
127 pathMap,
128 nameMap,
129 aliasRoute,
130 parent,
131 record.path || '/' // matchAs
132 )
133 })
134 }
135
136 if (!pathMap[record.path]) {
137 pathList.push(record.path)
138 pathMap[record.path] = record
139 }
140
141 if (name) {
142 if (!nameMap[name]) {
143 nameMap[name] = record
144 } else if (process.env.NODE_ENV !== 'production' && !matchAs) {
145 warn(
146 false,
147 `Duplicate named routes definition: ` +
148 `{ name: "${name}", path: "${record.path}" }`
149 )
150 }
151 }
152}
153
154function compileRouteRegex (path: string, pathToRegexpOptions: PathToRegexpOptions): RouteRegExp {
155 const regex = Regexp(path, [], pathToRegexpOptions)
156 if (process.env.NODE_ENV !== 'production') {
157 const keys: any = Object.create(null)
158 regex.keys.forEach(key => {
159 warn(!keys[key.name], `Duplicate param keys in route with path: "${path}"`)
160 keys[key.name] = true
161 })
162 }
163 return regex
164}
165
166function normalizePath (path: string, parent?: RouteRecord, strict?: boolean): string {
167 if (!strict) path = path.replace(/\/$/, '')
168 if (path[0] === '/') return path
169 if (parent == null) return path
170 return cleanPath(`${parent.path}/${path}`)
171}