From: Eduardo San Martin Morote Date: Fri, 29 Mar 2019 21:49:18 +0000 (+0100) Subject: wip: resolving in matcher X-Git-Tag: v4.0.0-alpha.0~469 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=02b76853f892145ae3f3fd757d3a5edfbd49e94a;p=thirdparty%2Fvuejs%2Frouter.git wip: resolving in matcher --- diff --git a/explorations/html5.ts b/explorations/html5.ts index b0241d29..dff41b04 100644 --- a/explorations/html5.ts +++ b/explorations/html5.ts @@ -54,3 +54,7 @@ r.push({ id: 'no-name', }, }) + +r.push({ + hash: '#hey', +}) diff --git a/src/history/html5.ts b/src/history/html5.ts index 2dc939e5..c549532e 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -35,7 +35,6 @@ export default class HTML5History extends BaseHistory { to ) this.location = to - cs.warn('changed location to', this.location) } replace(to: HistoryLocation) { @@ -54,7 +53,6 @@ export default class HTML5History extends BaseHistory { to ) this.location = to - cs.warn('changed location to', this.location) } push(to: HistoryLocation, data?: HistoryState) { @@ -78,7 +76,6 @@ export default class HTML5History extends BaseHistory { cs.info('push', this.location, '->', to, 'with state', state) this.history.pushState(state, '', to) this.location = to - cs.warn('changed location to', this.location) } listen(callback: NavigationCallback) { @@ -93,7 +90,6 @@ export default class HTML5History extends BaseHistory { // we have the state from the old entry, not the current one being removed // TODO: correctly parse pathname this.location = state ? state._current : buildFullPath - cs.warn('changed location to', this.location) callback(this.location, from, { type: from === state._forward diff --git a/src/index.ts b/src/index.ts index 23b53070..2df39368 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ import BaseHistory from './history/base' -import pathToRegexp from 'path-to-regexp' +import { RouterMatcher } from './matcher' import { RouterLocation, RouteRecord, - ParamsType, - START_RECORD, + START_LOCATION_NORMALIZED, + RouterLocationNormalized, } from './types/index' interface RouterOptions { @@ -12,38 +12,16 @@ interface RouterOptions { routes: RouteRecord[] } -interface RouteMatcher { - re: RegExp - resolve: (params: ParamsType) => string - record: RouteRecord - keys: string[] -} - -function generateMatcher(record: RouteRecord) { - const keys: pathToRegexp.Key[] = [] - // TODO: if children use option end: false ? - const re = pathToRegexp(record.path, keys) - return { - re, - resolve: pathToRegexp.compile(record.path), - keys: keys.map(k => '' + k.name), - record, - } -} - -const START_MATCHER = generateMatcher(START_RECORD) - export class Router { protected history: BaseHistory - private routes: RouteMatcher[] - currentRoute: RouteRecord = START_RECORD - currentMatcher: RouteMatcher = START_MATCHER + private matcher: RouterMatcher + currentRoute: RouterLocationNormalized = START_LOCATION_NORMALIZED constructor(options: RouterOptions) { this.history = options.history this.history.ensureLocation() - this.routes = options.routes.map(generateMatcher) + this.matcher = new RouterMatcher(options.routes) } /** @@ -52,47 +30,10 @@ export class Router { */ push(to: RouterLocation) { // TODO: resolve URL - const path = this.resolve(to) + const path = this.matcher.resolve(to, this.currentRoute) // TODO: call hooks, guards - this.history.push( - path + - // TODO: test purposes only - '?value=' + - Math.round(Math.random() * 10) + - '#e' + - Math.round(Math.random() * 10) - ) + this.history.push(path) } getRouteRecord(location: RouterLocation) {} - - /** - * Transforms a RouterLocation object into a URL string. If a string is - * passed, it returns the string itself - * @param location RouterLocation to resolve to a url - */ - resolve(location: Readonly): string { - if (typeof location === 'string') return location - if ('path' in location) { - // TODO: convert query, hash, warn params - return location.path - } - - let matcher: RouteMatcher | void - if (!('name' in location)) { - // TODO: use current location - // location = {...location, name: this.} - matcher = this.routes.find(r => r.record.name === this.currentRoute.name) - // return '/using current location' - } else { - matcher = this.routes.find(r => r.record.name === location.name) - } - - if (!matcher) { - // TODO: error - throw new Error('No match for' + location) - } - - return matcher.resolve(location.params || {}) - } } diff --git a/src/matcher.ts b/src/matcher.ts index 55f88702..a47ac7ed 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -1,16 +1,16 @@ import pathToRegexp from 'path-to-regexp' import { RouteRecord, - ParamsType, - START_RECORD, + RouteParams, RouterLocation, RouterLocationNormalized, } from './types/index' +import { stringifyQuery } from './uitls' // TODO: rename interface RouteMatcher { re: RegExp - resolve: (params: ParamsType) => string + resolve: (params: RouteParams) => string record: RouteRecord keys: string[] } @@ -27,8 +27,6 @@ function generateMatcher(record: RouteRecord) { } } -const START_MATCHER = generateMatcher(START_RECORD) - export class RouterMatcher { private matchers: RouteMatcher[] = [] @@ -53,21 +51,36 @@ export class RouterMatcher { * passed, it returns the string itself * @param location RouterLocation to resolve to a url */ - resolve(location: Readonly): string { + resolve( + location: Readonly, + currentLocation: RouterLocationNormalized + ): string { if (typeof location === 'string') return location + if ('path' in location) { - // TODO: convert query, hash, warn params - return location.path + // TODO: warn missing params + return ( + location.path + stringifyQuery(location.query) + (location.hash || '') + ) } let matcher: RouteMatcher | void if (!('name' in location)) { // TODO: use current location // location = {...location, name: this.} - matcher = this.routes.find(r => r.record.name === this.currentRoute.name) + if (currentLocation.name) { + // we don't want to match an undefined name + matcher = this.matchers.find( + m => m.record.name === currentLocation.name + ) + } else { + matcher = this.matchers.find( + m => m.record.path === currentLocation.path + ) + } // return '/using current location' } else { - matcher = this.routes.find(r => r.record.name === location.name) + matcher = this.matchers.find(m => m.record.name === location.name) } if (!matcher) { diff --git a/src/types/index.ts b/src/types/index.ts index 3b609b9e..9a0cc59c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,9 +1,10 @@ type TODO = any -export type ParamsType = Record +export type RouteParams = Record +export type RouteQuery = Record // interface PropsTransformer { -// (params: ParamsType): any +// (params: RouteParams): any // } // export interface RouterLocation { @@ -28,19 +29,25 @@ export type RouterLocation = | string | { path: string + query?: RouteQuery + hash?: string } | { name: string - params?: ParamsType + params?: RouteParams + query?: RouteQuery + hash?: string } | { - params: ParamsType + params?: RouteParams + query?: RouteQuery + hash?: string } export interface RouterLocationNormalized { path: string name?: string - params: ParamsType + params: RouteParams query: TODO hash: TODO } @@ -69,6 +76,13 @@ export const START_RECORD: RouteRecord = { component: { render: h => h() }, } +export const START_LOCATION_NORMALIZED: RouterLocationNormalized = { + path: '/', + params: {}, + query: {}, + hash: '', +} + export enum NavigationType { back, forward, diff --git a/src/uitls/index.ts b/src/uitls/index.ts new file mode 100644 index 00000000..99c787c4 --- /dev/null +++ b/src/uitls/index.ts @@ -0,0 +1,13 @@ +import { RouteQuery } from '../types' + +export function stringifyQuery(query: RouteQuery | void): string { + if (!query) return '' + + let search = '?' + for (const key in query) { + search += `${key}=${query[key]}` + } + + // no query means empty string + return search === '?' ? '' : '' +}