From: ggwujun Date: Thu, 13 May 2021 08:04:36 +0000 (-0500) Subject: fix(hash): allow characters after `#` in base e.g. `#!` (#925) X-Git-Tag: v4.0.8~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c91dd64f7c3e08f1603f506040ccf505fd9a9568;p=thirdparty%2Fvuejs%2Frouter.git fix(hash): allow characters after `#` in base e.g. `#!` (#925) Co-authored-by: dashixiong <876337269@qq.com> Co-authored-by: Eduardo San Martin Morote --- diff --git a/__tests__/history/html5.spec.ts b/__tests__/history/html5.spec.ts index 7df3b381..4eb446a3 100644 --- a/__tests__/history/html5.spec.ts +++ b/__tests__/history/html5.spec.ts @@ -16,6 +16,11 @@ describe('History HTMl5', () => { dom = createDom() }) + beforeEach(() => { + // empty the state to simulate an initial navigation by default + window.history.replaceState(null, '', '') + }) + afterAll(() => { dom.window.close() }) @@ -30,6 +35,9 @@ describe('History HTMl5', () => { it('handles a basic base', () => { expect(createWebHistory().base).toBe('') expect(createWebHistory('/').base).toBe('') + expect(createWebHistory('/#').base).toBe('/#') + expect(createWebHistory('#!').base).toBe('#!') + expect(createWebHistory('#other').base).toBe('#other') }) it('handles a base tag', () => { @@ -68,11 +76,15 @@ describe('History HTMl5', () => { it('handles a single hash base', () => { expect(createWebHistory('#').base).toBe('#') expect(createWebHistory('#/').base).toBe('#') + expect(createWebHistory('#!/').base).toBe('#!') + expect(createWebHistory('#other/').base).toBe('#other') }) it('handles a non-empty hash base', () => { expect(createWebHistory('#/bar').base).toBe('#/bar') expect(createWebHistory('#/bar/').base).toBe('#/bar') + expect(createWebHistory('#!/bar/').base).toBe('#!/bar') + expect(createWebHistory('#other/bar/').base).toBe('#other/bar') }) it('prepends the host to support // urls', () => { @@ -93,29 +105,95 @@ describe('History HTMl5', () => { spy.mockRestore() }) - it('calls push with hash part of the url with a base', () => { - dom.reconfigure({ url: 'file:///usr/etc/index.html' }) - let history = createWebHistory('#') - let spy = jest.spyOn(window.history, 'pushState') - history.push('/foo') - expect(spy).toHaveBeenCalledWith( - expect.anything(), - expect.any(String), - '#/foo' - ) - spy.mockRestore() - }) + describe('specific to base containing a hash', () => { + it('calls push with hash part of the url with a base', () => { + dom.reconfigure({ url: 'file:///usr/etc/index.html' }) + let initialSpy = jest.spyOn(window.history, 'replaceState') + let history = createWebHistory('#') + // initial navigation + expect(initialSpy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#/' + ) + let spy = jest.spyOn(window.history, 'pushState') + history.push('/foo') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#/foo' + ) + spy.mockRestore() + initialSpy.mockRestore() + }) - it('works with something after the hash in the base', () => { - dom.reconfigure({ url: 'file:///usr/etc/index.html' }) - let history = createWebHistory('#something') - let spy = jest.spyOn(window.history, 'pushState') - history.push('/foo') - expect(spy).toHaveBeenCalledWith( - expect.anything(), - expect.any(String), - '#something/foo' - ) - spy.mockRestore() + it('works with something after the hash in the base', () => { + dom.reconfigure({ url: 'file:///usr/etc/index.html' }) + let initialSpy = jest.spyOn(window.history, 'replaceState') + let history = createWebHistory('#something') + // initial navigation + expect(initialSpy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#something/' + ) + let spy = jest.spyOn(window.history, 'pushState') + history.push('/foo') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#something/foo' + ) + spy.mockRestore() + initialSpy.mockRestore() + }) + + it('works with #! and on a file with initial location', () => { + dom.reconfigure({ url: 'file:///usr/etc/index.html#!/foo' }) + let spy = jest.spyOn(window.history, 'replaceState') + createWebHistory('#!') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#!/foo' + ) + spy.mockRestore() + }) + + it('works with #other', () => { + dom.reconfigure({ url: 'file:///usr/etc/index.html' }) + let spy = jest.spyOn(window.history, 'replaceState') + createWebHistory('#other') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#other/' + ) + spy.mockRestore() + }) + + it('works with custom#other in domain', () => { + dom.reconfigure({ url: 'https://esm.dev/custom' }) + let spy = jest.spyOn(window.history, 'replaceState') + createWebHistory('custom#other') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#other/' + ) + spy.mockRestore() + }) + + it('works with #! and a host with initial location', () => { + dom.reconfigure({ url: 'https://esm.dev/#!/foo' }) + let spy = jest.spyOn(window.history, 'replaceState') + createWebHistory('/#!') + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.any(String), + '#!/foo' + ) + spy.mockRestore() + }) }) }) diff --git a/src/history/html5.ts b/src/history/html5.ts index 3b4c8be3..6dc3daa7 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -39,11 +39,14 @@ function createCurrentLocation( location: Location ): HistoryLocation { const { pathname, search, hash } = location - // allows hash based url + // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end const hashPos = base.indexOf('#') if (hashPos > -1) { + let slicePos = hash.includes(base.slice(hashPos)) + ? base.slice(hashPos).length + : 1 + let pathFromHash = hash.slice(slicePos) // prepend the starting slash to hash so the url starts with /# - let pathFromHash = hash.slice(1) if (pathFromHash[0] !== '/') pathFromHash = '/' + pathFromHash return stripBase(pathFromHash, '') }