From: Eduardo San Martin Morote Date: Thu, 18 Jun 2020 07:52:59 +0000 (+0200) Subject: fix: use assign to align with Vue browser support (#311) X-Git-Tag: v4.0.0-alpha.13~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f80b670d4dac30323221fcb2f93137ffd874c51b;p=thirdparty%2Fvuejs%2Frouter.git fix: use assign to align with Vue browser support (#311) * fix: use assign to align with Vue browser support Close #304 * fix: use correct imports * fix: more missed objects * fix: add missing copy --- diff --git a/src/RouterLink.ts b/src/RouterLink.ts index 43acfc7b..6b6239c3 100644 --- a/src/RouterLink.ts +++ b/src/RouterLink.ts @@ -12,6 +12,7 @@ import { RouteLocationRaw, VueUseOptions, RouteLocation } from './types' import { isSameLocationObject, isSameRouteRecord } from './location' import { routerKey, routeLocationKey } from './injectionSymbols' import { RouteRecord } from './matcher/types' +import { assign } from './utils' export interface RouterLinkProps { to: RouteLocationRaw @@ -126,13 +127,17 @@ export const RouterLinkImpl = defineComponent({ ? children : h( 'a', - { - 'aria-current': link.isExactActive ? 'page' : null, - onClick: link.navigate, - href: link.href, - ...attrs, - class: elClass.value, - }, + assign( + { + 'aria-current': link.isExactActive ? 'page' : null, + onClick: link.navigate, + href: link.href, + }, + attrs, + { + class: elClass.value, + } + ), children ) } diff --git a/src/RouterView.ts b/src/RouterView.ts index fb7fda9c..6cc477ae 100644 --- a/src/RouterView.ts +++ b/src/RouterView.ts @@ -15,6 +15,7 @@ import { viewDepthKey, routeLocationKey, } from './injectionSymbols' +import { assign } from './utils' export interface RouterViewProps { name?: string @@ -86,14 +87,17 @@ export const RouterViewImpl = defineComponent({ } let Component = ViewComponent.value - const componentProps: Parameters[1] = { + const componentProps: Parameters[1] = assign( + {}, // only compute props if there is a matched record - ...(Component && propsData.value), - ...attrs, - onVnodeMounted, - onVnodeUnmounted, - ref: viewRef, - } + Component && propsData.value, + attrs, + { + onVnodeMounted, + onVnodeUnmounted, + ref: viewRef, + } + ) // NOTE: we could also not render if there is no route match const children = diff --git a/src/errors.ts b/src/errors.ts index a66f0640..fce067c2 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -4,6 +4,7 @@ import { RouteLocationRaw, RouteLocationNormalized, } from './types' +import { assign } from './utils' /** * order is important to make it backwards compatible with v3 @@ -82,13 +83,13 @@ export function createRouterError( params: Omit ): E { if (__DEV__ || !__BROWSER__) { - return Object.assign( + return assign( new Error(ErrorTypeMessages[type](params as any)), { type }, params ) as E } else { - return Object.assign(new Error(), { type }, params) as E + return assign(new Error(), { type }, params) as E } } diff --git a/src/history/html5.ts b/src/history/html5.ts index 863cca60..8a53c064 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -16,6 +16,7 @@ import { } from '../scrollBehavior' import { warn } from '../warning' import { stripBase } from '../location' +import { assign } from '../utils' type PopStateListener = (this: Window, ev: PopStateEvent) => any @@ -124,10 +125,7 @@ function useHistoryListeners( const { history } = window if (!history.state) return history.replaceState( - { - ...history.state, - scroll: computeScrollPosition(), - }, + assign({}, history.state, { scroll: computeScrollPosition() }), '' ) } @@ -218,18 +216,19 @@ function useHistoryStateNavigation(base: string) { function replace(to: RawHistoryLocation, data?: HistoryState) { const normalized = normalizeHistoryLocation(to) - const state: StateEntry = { - ...history.state, - ...buildState( + const state: StateEntry = assign( + {}, + history.state, + buildState( historyState.value.back, // keep back and forward entries but override current position normalized, historyState.value.forward, true ), - ...data, - position: historyState.value.position, - } + data, + { position: historyState.value.position } + ) changeLocation(normalized, state, true) location.value = normalized @@ -240,18 +239,20 @@ function useHistoryStateNavigation(base: string) { // Add to current entry the information of where we are going // as well as saving the current position - const currentState: StateEntry = { - ...history.state, + const currentState: StateEntry = assign({}, history.state, { forward: normalized, scroll: computeScrollPosition(), - } + }) changeLocation(currentState.current, currentState, true) - const state: StateEntry = { - ...buildState(location.value, normalized, null), - position: currentState.position + 1, - ...data, - } + const state: StateEntry = assign( + {}, + buildState(location.value, normalized, null), + { + position: currentState.position + 1, + }, + data + ) changeLocation(normalized, state, false) location.value = normalized @@ -280,16 +281,18 @@ export function createWebHistory(base?: string): RouterHistory { if (!triggerListeners) historyListeners.pauseListeners() history.go(delta) } - const routerHistory: RouterHistory = { - // it's overridden right after - // @ts-ignore - location: '', - base, - go, - ...historyNavigation, - ...historyListeners, - } + const routerHistory: RouterHistory = assign( + { + // it's overridden right after + location: ('' as unknown) as HistoryLocationNormalized, + base, + go, + }, + + historyNavigation, + historyListeners + ) Object.defineProperty(routerHistory, 'location', { get: () => historyNavigation.location.value, diff --git a/src/matcher/index.ts b/src/matcher/index.ts index dae90ca3..6ae1734a 100644 --- a/src/matcher/index.ts +++ b/src/matcher/index.ts @@ -18,6 +18,7 @@ import { _PathParserOptions, } from './pathParserRanker' import { warn } from '../warning' +import { assign } from '../utils' let noop = () => {} @@ -70,21 +71,22 @@ export function createRouterMatcher( const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias! for (const alias of aliases) { - normalizedRecords.push({ - ...mainNormalizedRecord, - // this allows us to hold a copy of the `components` option - // so that async components cache is hold on the original record - components: originalRecord - ? originalRecord.record.components - : mainNormalizedRecord.components, - path: alias, - // we might be the child of an alias - aliasOf: originalRecord - ? originalRecord.record - : mainNormalizedRecord, - // the aliases are always of the same kind as the original since they - // are defined on the same record - } as typeof mainNormalizedRecord) + normalizedRecords.push( + assign({}, mainNormalizedRecord, { + // this allows us to hold a copy of the `components` option + // so that async components cache is hold on the original record + components: originalRecord + ? originalRecord.record.components + : mainNormalizedRecord.components, + path: alias, + // we might be the child of an alias + aliasOf: originalRecord + ? originalRecord.record + : mainNormalizedRecord, + // the aliases are always of the same kind as the original since they + // are defined on the same record + }) as typeof mainNormalizedRecord + ) } } @@ -219,13 +221,14 @@ export function createRouterMatcher( }) name = matcher.record.name - params = { - ...paramsFromLocation( + params = assign( + // paramsFromLocation is a new object + paramsFromLocation( currentLocation.params, matcher.keys.map(k => k.name) ), - ...location.params, - } + location.params + ) // throws if cannot be stringified path = matcher.stringify(params) } else if ('path' in location) { @@ -261,7 +264,7 @@ export function createRouterMatcher( name = matcher.record.name // since we are navigating to the same location, we don't need to pick the // params like when `name` is provided - params = { ...currentLocation.params, ...location.params } + params = assign({}, currentLocation.params, location.params) path = matcher.stringify(params) } @@ -303,7 +306,8 @@ function paramsFromLocation( } /** - * Normalizes a RouteRecordRaw. Transforms the `redirect` option into a `beforeEnter` + * Normalizes a RouteRecordRaw. Transforms the `redirect` option into a + * `beforeEnter`. This function creates a copy * @param record * @returns the normalized version */ @@ -319,15 +323,11 @@ export function normalizeRouteRecord( } if ('redirect' in record) { - return { - ...commonInitialValues, - redirect: record.redirect, - } + return assign(commonInitialValues, { redirect: record.redirect }) } else { const components = 'components' in record ? record.components : { default: record.component } - return { - ...commonInitialValues, + return assign(commonInitialValues, { beforeEnter: record.beforeEnter, props: normalizeRecordProps(record), children: record.children || [], @@ -335,7 +335,7 @@ export function normalizeRouteRecord( leaveGuards: [], updateGuards: [], components, - } + }) } } @@ -381,10 +381,7 @@ function isAliasRecord(record: RouteRecordMatcher | undefined): boolean { */ function mergeMetaFields(matched: MatcherLocation['matched']) { return matched.reduce( - (meta, record) => ({ - ...meta, - ...record.meta, - }), + (meta, record) => assign(meta, record.meta), {} as MatcherLocation['meta'] ) } diff --git a/src/matcher/pathMatcher.ts b/src/matcher/pathMatcher.ts index f9bfcf6e..aae2b782 100644 --- a/src/matcher/pathMatcher.ts +++ b/src/matcher/pathMatcher.ts @@ -6,6 +6,7 @@ import { } from './pathParserRanker' import { tokenizePath } from './pathTokenizer' import { warn } from '../warning' +import { assign } from '../utils' export interface RouteRecordMatcher extends PathParser { record: RouteRecord @@ -34,14 +35,13 @@ export function createRouteRecordMatcher( } } - const matcher: RouteRecordMatcher = { - ...parser, + const matcher: RouteRecordMatcher = assign(parser, { record, parent, // these needs to be populated by the parent children: [], alias: [], - } + }) if (parent) { // both are aliases or both are not aliases diff --git a/src/matcher/pathParserRanker.ts b/src/matcher/pathParserRanker.ts index 1519c4f8..a5f4a1d3 100644 --- a/src/matcher/pathParserRanker.ts +++ b/src/matcher/pathParserRanker.ts @@ -1,4 +1,5 @@ import { Token, TokenType } from './pathTokenizer' +import { assign } from '../utils' export type PathParams = Record @@ -108,10 +109,7 @@ export function tokensToParser( segments: Array, extraOptions?: _PathParserOptions ): PathParser { - const options = { - ...BASE_PATH_PARSER_OPTIONS, - ...extraOptions, - } + const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions) // the amount of scores is the same as the length of segments except for the root segment "/" let score: Array = [] diff --git a/src/router.ts b/src/router.ts index 1358974b..e8a56e61 100644 --- a/src/router.ts +++ b/src/router.ts @@ -30,7 +30,7 @@ import { NavigationFailure, NavigationRedirectError, } from './errors' -import { applyToParams, isBrowser } from './utils' +import { applyToParams, isBrowser, assign } from './utils' import { useCallbacks } from './utils/callbacks' import { encodeParam, decode, encodeHash } from './encoding' import { @@ -249,20 +249,12 @@ export function createRouter(options: RouterOptions): Router { } } - return { - // fullPath: locationNormalized.fullPath, - // query: locationNormalized.query, - // hash: locationNormalized.hash, - ...locationNormalized, - ...matchedRoute, - // path: matchedRoute.path, - // name: matchedRoute.name, - // meta: matchedRoute.meta, - // matched: matchedRoute.matched, + // locationNormalized is always a new object + return assign(locationNormalized, matchedRoute, { params: decodeParams(matchedRoute.params), redirectedFrom: undefined, href: routerHistory.base + locationNormalized.fullPath, - } + }) } let matcherLocation: MatcherLocationRaw @@ -281,15 +273,13 @@ export function createRouter(options: RouterOptions): Router { }" was passed with params but they will be ignored. Use a named route alongside params instead.` ) } - matcherLocation = { - ...rawLocation, + matcherLocation = assign({}, rawLocation, { path: parseURL(parseQuery, rawLocation.path, currentLocation.path).path, - } + }) } else { - matcherLocation = { - ...rawLocation, + matcherLocation = assign({}, rawLocation, { params: encodeParams(rawLocation.params), - } + }) } let matchedRoute = matcher.resolve(matcherLocation, currentLocation) @@ -307,11 +297,13 @@ export function createRouter(options: RouterOptions): Router { ? normalizeParams(rawLocation.params) : decodeParams(matchedRoute.params) - const fullPath = stringifyURL(stringifyQuery, { - ...rawLocation, - hash, - path: matchedRoute.path, - }) + const fullPath = stringifyURL( + stringifyQuery, + assign({}, rawLocation, { + hash, + path: matchedRoute.path, + }) + ) if (__DEV__) { let href = routerHistory.base + fullPath @@ -328,22 +320,26 @@ export function createRouter(options: RouterOptions): Router { } } - return { - fullPath, - // keep the hash encoded so fullPath is effectively path + encodedQuery + - // hash - hash, - query: normalizeQuery(rawLocation.query), - ...matchedRoute, - redirectedFrom: undefined, - href: routerHistory.base + fullPath, - } + return assign( + { + fullPath, + // keep the hash encoded so fullPath is effectively path + encodedQuery + + // hash + hash, + query: normalizeQuery(rawLocation.query), + }, + matchedRoute, + { + redirectedFrom: undefined, + href: routerHistory.base + fullPath, + } + ) } function locationAsObject( to: RouteLocationRaw | RouteLocationNormalized ): Exclude | RouteLocationNormalized { - return typeof to === 'string' ? { path: to } : to + return typeof to === 'string' ? { path: to } : assign({}, to) } function push(to: RouteLocationRaw | RouteLocation) { @@ -351,7 +347,7 @@ export function createRouter(options: RouterOptions): Router { } function replace(to: RouteLocationRaw | RouteLocationNormalized) { - return push({ ...locationAsObject(to), replace: true }) + return push(assign(locationAsObject(to), { replace: true })) } function pushWithRedirect( @@ -391,18 +387,21 @@ export function createRouter(options: RouterOptions): Router { return Promise.reject(new Error('Invalid redirect')) } return pushWithRedirect( - { + assign( + {}, // having a path here would be a problem with relative locations but // at the same time it doesn't make sense for a redirect to be // relative (no name, no path) because it would create an infinite // loop. Since newTargetLocation must either have a `path` or a // `name`, this will never happen - ...targetLocation, - ...newTargetLocation, - state: data, - force, - replace, - }, + targetLocation, + newTargetLocation, + { + state: data, + force, + replace, + } + ), // keep original redirectedFrom if it exists redirectedFrom || targetLocation ) @@ -459,12 +458,11 @@ export function createRouter(options: RouterOptions): Router { // preserve the original redirectedFrom if any return pushWithRedirect( // keep options - { - ...locationAsObject(failure.to), + assign(locationAsObject(failure.to), { state: data, force, replace, - }, + }), redirectedFrom || toLocation ) } else { @@ -638,10 +636,15 @@ export function createRouter(options: RouterOptions): Router { // on the initial navigation, we want to reuse the scroll position from // history state if it exists if (replace || isFirstNavigation) - routerHistory.replace(toLocation, { - scroll: isFirstNavigation && state && state.scroll, - ...data, - }) + routerHistory.replace( + toLocation, + assign( + { + scroll: isFirstNavigation && state && state.scroll, + }, + data + ) + ) else routerHistory.push(toLocation, data) } diff --git a/src/utils/index.ts b/src/utils/index.ts index de75374b..ee3385cb 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,6 +7,8 @@ export function isESModule(obj: any): obj is { default: RouteComponent } { return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') } +export const assign = Object.assign + export function applyToParams( fn: (v: string | number) => string, params: RouteParamsRaw | undefined