123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- /* @flow */
-
- import type VueRouter from './index'
- import { resolvePath } from './util/path'
- import { assert, warn } from './util/warn'
- import { createRoute } from './util/route'
- import { fillParams } from './util/params'
- import { createRouteMap } from './create-route-map'
- import { normalizeLocation } from './util/location'
-
- export type Matcher = {
- match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
- addRoutes: (routes: Array<RouteConfig>) => void;
- };
-
- export function createMatcher (
- routes: Array<RouteConfig>,
- router: VueRouter
- ): Matcher {
- const { pathList, pathMap, nameMap } = createRouteMap(routes)
-
- function addRoutes (routes) {
- createRouteMap(routes, pathList, pathMap, nameMap)
- }
-
- function match (
- raw: RawLocation,
- currentRoute?: Route,
- redirectedFrom?: Location
- ): Route {
- const location = normalizeLocation(raw, currentRoute, false, router)
- const { name } = location
-
- if (name) {
- const record = nameMap[name]
- if (process.env.NODE_ENV !== 'production') {
- warn(record, `Route with name '${name}' does not exist`)
- }
- if (!record) return _createRoute(null, location)
- const paramNames = record.regex.keys
- .filter(key => !key.optional)
- .map(key => key.name)
-
- if (typeof location.params !== 'object') {
- location.params = {}
- }
-
- if (currentRoute && typeof currentRoute.params === 'object') {
- for (const key in currentRoute.params) {
- if (!(key in location.params) && paramNames.indexOf(key) > -1) {
- location.params[key] = currentRoute.params[key]
- }
- }
- }
-
- if (record) {
- location.path = fillParams(record.path, location.params, `named route "${name}"`)
- return _createRoute(record, location, redirectedFrom)
- }
- } else if (location.path) {
- location.params = {}
- for (let i = 0; i < pathList.length; i++) {
- const path = pathList[i]
- const record = pathMap[path]
- if (matchRoute(record.regex, location.path, location.params)) {
- return _createRoute(record, location, redirectedFrom)
- }
- }
- }
- // no match
- return _createRoute(null, location)
- }
-
- function redirect (
- record: RouteRecord,
- location: Location
- ): Route {
- const originalRedirect = record.redirect
- let redirect = typeof originalRedirect === 'function'
- ? originalRedirect(createRoute(record, location, null, router))
- : originalRedirect
-
- if (typeof redirect === 'string') {
- redirect = { path: redirect }
- }
-
- if (!redirect || typeof redirect !== 'object') {
- if (process.env.NODE_ENV !== 'production') {
- warn(
- false, `invalid redirect option: ${JSON.stringify(redirect)}`
- )
- }
- return _createRoute(null, location)
- }
-
- const re: Object = redirect
- const { name, path } = re
- let { query, hash, params } = location
- query = re.hasOwnProperty('query') ? re.query : query
- hash = re.hasOwnProperty('hash') ? re.hash : hash
- params = re.hasOwnProperty('params') ? re.params : params
-
- if (name) {
- // resolved named direct
- const targetRecord = nameMap[name]
- if (process.env.NODE_ENV !== 'production') {
- assert(targetRecord, `redirect failed: named route "${name}" not found.`)
- }
- return match({
- _normalized: true,
- name,
- query,
- hash,
- params
- }, undefined, location)
- } else if (path) {
- // 1. resolve relative redirect
- const rawPath = resolveRecordPath(path, record)
- // 2. resolve params
- const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`)
- // 3. rematch with existing query and hash
- return match({
- _normalized: true,
- path: resolvedPath,
- query,
- hash
- }, undefined, location)
- } else {
- if (process.env.NODE_ENV !== 'production') {
- warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`)
- }
- return _createRoute(null, location)
- }
- }
-
- function alias (
- record: RouteRecord,
- location: Location,
- matchAs: string
- ): Route {
- const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`)
- const aliasedMatch = match({
- _normalized: true,
- path: aliasedPath
- })
- if (aliasedMatch) {
- const matched = aliasedMatch.matched
- const aliasedRecord = matched[matched.length - 1]
- location.params = aliasedMatch.params
- return _createRoute(aliasedRecord, location)
- }
- return _createRoute(null, location)
- }
-
- function _createRoute (
- record: ?RouteRecord,
- location: Location,
- redirectedFrom?: Location
- ): Route {
- if (record && record.redirect) {
- return redirect(record, redirectedFrom || location)
- }
- if (record && record.matchAs) {
- return alias(record, location, record.matchAs)
- }
- return createRoute(record, location, redirectedFrom, router)
- }
-
- return {
- match,
- addRoutes
- }
- }
-
- function matchRoute (
- regex: RouteRegExp,
- path: string,
- params: Object
- ): boolean {
- const m = path.match(regex)
-
- if (!m) {
- return false
- } else if (!params) {
- return true
- }
-
- for (let i = 1, len = m.length; i < len; ++i) {
- const key = regex.keys[i - 1]
- const val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i]
- if (key) {
- // Fix #1994: using * with props: true generates a param named 0
- params[key.name || 'pathMatch'] = val
- }
- }
-
- return true
- }
-
- function resolveRecordPath (path: string, record: RouteRecord): string {
- return resolvePath(path, record.parent ? record.parent.path : '/', true)
- }
|