From: Eduardo San Martin Morote Date: Mon, 3 Aug 2020 08:54:39 +0000 (+0200) Subject: feat(url): simple resolve relative location X-Git-Tag: v4.0.0-beta.5~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69c44db3fd5363a833675b4b0ef14f97ac691af6;p=thirdparty%2Fvuejs%2Frouter.git feat(url): simple resolve relative location --- diff --git a/__tests__/location.spec.ts b/__tests__/location.spec.ts index 7e798d13..3d45a8ba 100644 --- a/__tests__/location.spec.ts +++ b/__tests__/location.spec.ts @@ -5,8 +5,10 @@ import { stripBase, isSameRouteLocationParams, isSameRouteLocation, + resolveRelativePath, } from '../src/location' import { RouteLocationNormalizedLoaded } from 'src' +import { mockWarn } from 'jest-mock-warn' describe('parseURL', () => { let parseURL = originalParseURL.bind(null, parseQuery) @@ -283,3 +285,61 @@ describe('isSameRouteLocation', () => { ).toBe(true) }) }) + +describe('resolveRelativePath', () => { + mockWarn() + it('resolves relative direct path', () => { + expect(resolveRelativePath('add', '/users/posva')).toBe('/users/add') + expect(resolveRelativePath('add', '/users/posva/')).toBe('/users/posva/add') + expect(resolveRelativePath('add', '/users/posva/thing')).toBe( + '/users/posva/add' + ) + }) + + it('resolves relative direct path with .', () => { + expect(resolveRelativePath('./add', '/users/posva')).toBe('/users/add') + expect(resolveRelativePath('./add', '/users/posva/')).toBe( + '/users/posva/add' + ) + expect(resolveRelativePath('./add', '/users/posva/thing')).toBe( + '/users/posva/add' + ) + }) + + it('resolves relative path with ..', () => { + expect(resolveRelativePath('../add', '/users/posva')).toBe('/add') + expect(resolveRelativePath('../add', '/users/posva/')).toBe('/users/add') + expect(resolveRelativePath('../add', '/users/posva/thing')).toBe( + '/users/add' + ) + }) + + it('resolves multiple relative paths with ..', () => { + expect(resolveRelativePath('../../add', '/users/posva')).toBe('/add') + expect(resolveRelativePath('../../add', '/users/posva/')).toBe('/add') + expect(resolveRelativePath('../../add', '/users/posva/thing')).toBe('/add') + expect(resolveRelativePath('../../../add', '/users/posva')).toBe('/add') + }) + + it('works with the root', () => { + expect(resolveRelativePath('add', '/')).toBe('/add') + expect(resolveRelativePath('./add', '/')).toBe('/add') + expect(resolveRelativePath('../add', '/')).toBe('/add') + expect(resolveRelativePath('.././add', '/')).toBe('/add') + expect(resolveRelativePath('./../add', '/')).toBe('/add') + expect(resolveRelativePath('../../add', '/')).toBe('/add') + expect(resolveRelativePath('../../../add', '/')).toBe('/add') + }) + + it('ignores it location is absolute', () => { + expect(resolveRelativePath('/add', '/users/posva')).toBe('/add') + }) + + it('warns if from path is not absolute', () => { + resolveRelativePath('path', 'other') + resolveRelativePath('path', './other') + resolveRelativePath('path', '../other') + + expect('Cannot resolve').toHaveBeenWarnedTimes(3) + }) +}) diff --git a/src/location.ts b/src/location.ts index 3d7155b4..3e67b367 100644 --- a/src/location.ts +++ b/src/location.ts @@ -5,6 +5,7 @@ import { RouteParamValue, } from './types' import { RouteRecord } from './matcher/types' +import { warn } from './warning' /** * Location object returned by {@link `parseURL`}. @@ -186,11 +187,51 @@ function isSameRouteLocationParamsValue( * Check if two arrays are the same or if an array with one single entry is the * same as another primitive value. Used to check query and parameters * - * @param a array of values - * @param b array of values or a single value + * @param a - array of values + * @param b - array of values or a single value */ function isEquivalentArray(a: T[], b: T[] | T): boolean { return Array.isArray(b) ? a.length === b.length && a.every((value, i) => value === b[i]) : a.length === 1 && a[0] === b } + +/** + * Resolves a relative path that starts with `.`. + * + * @param to - path location we are resolving + * @param from - currentLocation.path, should start with `/` + */ +export function resolveRelativePath(to: string, from: string): string { + if (to.startsWith('/')) return to + if (__DEV__ && !from.startsWith('/')) { + warn( + `Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".` + ) + return to + } + + const fromSegments = from.split('/') + const toSegments = to.split('/') + + let position = fromSegments.length - 1 + let toPosition: number + let segment: string + + for (toPosition = 0; toPosition < toSegments.length; toPosition++) { + segment = toSegments[toPosition] + // can't go below zero + if (position === 1 || segment === '.') continue + if (segment === '..') position-- + // found something that is not relative path + else break + } + + return ( + fromSegments.slice(0, position).join('/') + + '/' + + toSegments + .slice(toPosition - (toPosition === toSegments.length ? 1 : 0)) + .join('/') + ) +}