]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: add base option to history
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 14 Oct 2019 18:09:35 +0000 (20:09 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 14 Oct 2019 18:09:35 +0000 (20:09 +0200)
__tests__/router-link.spec.ts
__tests__/router.spec.ts
src/components/Link.ts
src/history/common.ts
src/history/hash.ts
src/history/html5.ts
src/history/memory.ts
src/index.ts
src/router.ts

index 5bef966c511285e521e2d659ff21028bd330c9da..5416a5a3369dcffd25cf753770f67b714ae6d848 100644 (file)
@@ -58,6 +58,9 @@ describe('RouterLink', () => {
   ) {
     const router = {
       history: createMemoryHistory(),
+      createHref(to: RouteLocationNormalized): string {
+        return this.history.base + to.fullPath
+      },
       resolve: jest.fn(),
       push: jest.fn(),
     }
index 60d326a3aa67034a421bec1cf492b97f024cb38a..105e6dce2eec641a1e0917cdc273b3d7a727886c 100644 (file)
@@ -1,5 +1,5 @@
 import fakePromise from 'faked-promise'
-import { Router, createMemoryHistory } from '../src'
+import { Router, createMemoryHistory, createHistory } from '../src'
 import { NavigationCancelled } from '../src/errors'
 import { createDom, components, tick } from './utils'
 import { RouteRecord, RouteLocation } from '../src/types'
@@ -265,5 +265,51 @@ describe('Router', () => {
     })
   })
 
+  it('allows base option in abstract history', async () => {
+    const history = createMemoryHistory('/app/')
+    const router = new Router({ history, routes })
+    expect(router.currentRoute).toEqual({
+      name: undefined,
+      fullPath: '/',
+      hash: '',
+      params: {},
+      path: '/',
+      query: {},
+      meta: {},
+    })
+    await router.replace('/foo')
+    expect(router.currentRoute).toMatchObject({
+      name: 'Foo',
+      fullPath: '/foo',
+      hash: '',
+      params: {},
+      path: '/foo',
+      query: {},
+    })
+  })
+
+  it('allows base option with html5 history', async () => {
+    const history = createHistory('/app/')
+    const router = new Router({ history, routes })
+    expect(router.currentRoute).toEqual({
+      name: undefined,
+      fullPath: '/',
+      hash: '',
+      params: {},
+      path: '/',
+      query: {},
+      meta: {},
+    })
+    await router.replace('/foo')
+    expect(router.currentRoute).toMatchObject({
+      name: 'Foo',
+      fullPath: '/foo',
+      hash: '',
+      params: {},
+      path: '/foo',
+      query: {},
+    })
+  })
+
   // it('redirects with route record redirect')
 })
index 8824ef868c1c3c01e3d82edc31eb5873ee6f350a..1efa15806561ee446e5ef11f91a02a44442830a6 100644 (file)
@@ -20,6 +20,7 @@ const Link: Component = {
     const to = this.to as RouteLocation
 
     const route = router.resolve(to)
+    const href = router.createHref(route)
 
     // TODO: active classes
     // TODO: handle replace prop
@@ -35,7 +36,7 @@ const Link: Component = {
 
     const data: any = {
       on,
-      attrs: { href: route.fullPath },
+      attrs: { href },
     }
 
     // @ts-ignore
index a78f3e657d65ad793ca123811fd77077434be3b0..50bef9fe177ba9aa04a8db71463af6ae0b12e2ce 100644 (file)
@@ -277,3 +277,15 @@ export function normalizeLocation(
       hash: location.hash || '',
     }
 }
+
+/**
+ * Strips off the base from the beginning of a location.pathname
+ * @param pathname location.pathname
+ * @param base base to strip off
+ */
+export function stripBase(pathname: string, base: string): string {
+  return (
+    (base && pathname.indexOf(base) === 0 && pathname.replace(base, '')) ||
+    pathname
+  )
+}
index 706f5fbebd4f47342fea01b3e3fdd42717e5184c..1c8ac40ab8d5e7fcfafff125b6b083a9e56331e2 100644 (file)
@@ -1,8 +1,7 @@
 import { RouterHistory } from './common'
 import createHistory from './html5'
 
-export default function createHashHistory(): RouterHistory {
+export default function createHashHistory(base: string = ''): RouterHistory {
   // Make sure this implementation is fine in terms of encoding, specially for IE11
-  // @ts-ignore: TODO: implement it in history first
-  return createHistory('/#/')
+  return createHistory('/#' + base)
 }
index c964c5bf76c465e9362a33ab328024dbfdc0943b..62759e77d77283bcb472048687e6cdf775bc5cb4 100644 (file)
@@ -3,10 +3,12 @@ import {
   NavigationCallback,
   parseQuery,
   normalizeLocation,
+  stripBase,
   NavigationType,
   NavigationDirection,
   HistoryLocationNormalized,
   HistoryState,
+  parseURL,
 } from './common'
 import { computeScrollPosition, ScrollToPosition } from '../utils/scroll'
 // import consola from 'consola'
@@ -24,7 +26,7 @@ interface StateEntry {
   scroll: ScrollToPosition | null
 }
 
-export default function createHistory(): RouterHistory {
+export default function createHistory(base: string = ''): RouterHistory {
   const { history } = window
 
   /**
@@ -35,11 +37,18 @@ export default function createHistory(): RouterHistory {
   function createCurrentLocation(
     location: Location
   ): HistoryLocationNormalized {
+    const { pathname, search, hash } = location
+    // allows hash based url
+    if (base.indexOf('#') > -1) {
+      // prepend the starting slash to hash so the url starts with /#
+      return parseURL(stripBase('/' + hash, base))
+    }
+    const path = stripBase(pathname, base)
     return {
-      fullPath: location.pathname + location.search + location.hash,
-      path: location.pathname,
-      query: parseQuery(location.search),
-      hash: location.hash,
+      fullPath: path + search + hash,
+      path,
+      query: parseQuery(search),
+      hash: hash,
     }
   }
 
@@ -136,9 +145,10 @@ export default function createHistory(): RouterHistory {
   function changeLocation(
     state: StateEntry,
     title: string,
-    url: string,
+    fullPath: string,
     replace: boolean
   ): void {
+    const url = base + fullPath
     try {
       // BROWSER QUIRK
       // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
@@ -159,8 +169,7 @@ export default function createHistory(): RouterHistory {
   const routerHistory: RouterHistory = {
     // it's overriden right after
     location,
-    // TODO: implement it
-    base: '/',
+    base,
 
     replace(to) {
       const normalized = normalizeLocation(to)
index 8ce4e3bbe2f05de584613d7a8c66c9ac015e97b6..634161eb11bd9b01e7709785219a8ceadc5e17c9 100644 (file)
@@ -14,7 +14,13 @@ import {
 // const cs = console
 // const cs = consola.withTag('abstract')
 
-export default function createMemoryHistory(): RouterHistory {
+/**
+ * Creates a in-memory based history. The main purporse of this history is to handle SSR. It starts in a special location that is nowhere.
+ * It's up to the user to replace that location with the starter location.
+ * @param base Base applied to all urls, defaults to '/'
+ * @returns a history object that can be passed to the router constructor
+ */
+export default function createMemoryHistory(base: string = ''): RouterHistory {
   let listeners: NavigationCallback[] = []
   // TODO: make sure this is right as the first location is nowhere so maybe this should be empty instead
   let queue: HistoryLocationNormalized[] = [START]
@@ -53,8 +59,8 @@ export default function createMemoryHistory(): RouterHistory {
   const routerHistory: RouterHistory = {
     // rewritten by Object.defineProperty
     location: START,
-    // TODO: implement it
-    base: '/',
+    // TODO: acutally use it
+    base,
 
     replace(to) {
       const toNormalized = normalizeLocation(to)
index a46e17b24287bf4bf5b50371dbb92dc1ae975e59..1046823606405b855b5348adc4653329a1031006 100644 (file)
@@ -2,6 +2,7 @@ import { Router, RouterOptions } from './router'
 import { PluginFunction, VueConstructor } from 'vue'
 import createHistory from './history/html5'
 import createMemoryHistory from './history/memory'
+import createHashHistory from './history/hash'
 import View from './components/View'
 import Link from './components/Link'
 
@@ -63,7 +64,7 @@ const plugin: PluginFunction<void> = Vue => {
     strats.created
 }
 
-export { Router, createHistory, createMemoryHistory, plugin }
+export { Router, createHistory, createMemoryHistory, createHashHistory, plugin }
 
 // TODO: refactor somewhere else
 // const inBrowser = typeof window !== 'undefined'
index babf70fc6fde82b2c8360a9c0e88abfb92c884f8..ab20592eb95822ff65f4c66b81f56b5ffbd2f7a7 100644 (file)
@@ -143,7 +143,7 @@ export class Router {
   resolve(
     to: RouteLocation,
     currentLocation?: RouteLocationNormalized /*, append?: boolean */
-  ) {
+  ): RouteLocationNormalized {
     if (typeof to === 'string')
       return this.resolveLocation(
         // TODO: refactor and remove import
@@ -158,6 +158,10 @@ export class Router {
     })
   }
 
+  createHref(to: RouteLocationNormalized): string {
+    return this.history.base + to.fullPath
+  }
+
   private resolveLocation(
     location: MatcherLocation & Required<RouteQueryAndHash>,
     currentLocation?: RouteLocationNormalized,