1 |
|
2 |
|
3 | import Regexp from 'path-to-regexp'
|
4 | import { cleanPath } from './util/path'
|
5 | import { assert, warn } from './util/warn'
|
6 |
|
7 | export 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 |
|
18 | const pathList: Array<string> = oldPathList || []
|
19 |
|
20 | const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)
|
21 |
|
22 | const nameMap: Dictionary<RouteRecord> = oldNameMap || Object.create(null)
|
23 |
|
24 | routes.forEach(route => {
|
25 | addRouteRecord(pathList, pathMap, nameMap, route)
|
26 | })
|
27 |
|
28 |
|
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 |
|
44 | function 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 |
|
93 |
|
94 |
|
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 || '/'
|
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 |
|
154 | function 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 |
|
166 | function 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 | }
|