]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix: use assign to align with Vue browser support (#311)
authorEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 18 Jun 2020 07:52:59 +0000 (09:52 +0200)
committerGitHub <noreply@github.com>
Thu, 18 Jun 2020 07:52:59 +0000 (09:52 +0200)
* fix: use assign to align with Vue browser support

Close #304

* fix: use correct imports

* fix: more missed objects

* fix: add missing copy

src/RouterLink.ts
src/RouterView.ts
src/errors.ts
src/history/html5.ts
src/matcher/index.ts
src/matcher/pathMatcher.ts
src/matcher/pathParserRanker.ts
src/router.ts
src/utils/index.ts

index 43acfc7b3dd743e42c475629d5ff285bbd7526d5..6b6239c325683e7879da393db5e5057ea8367e4a 100644 (file)
@@ -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
           )
     }
index fb7fda9cc0b26bf2da981df1ef9e8e606bd893b9..6cc477ae081f063d7409de110ffd99cd16c4ec91 100644 (file)
@@ -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<typeof h>[1] = {
+      const componentProps: Parameters<typeof h>[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 =
index a66f0640df8f81d72395cd7d10b386e499a50e20..fce067c2896d2247d7fbb72e80e340b5cefb4dbb 100644 (file)
@@ -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<E extends RouterError>(
   params: Omit<E, 'type' | keyof Error>
 ): 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
   }
 }
 
index 863cca6018289cf0e6889273e2cf1ec4b34efc16..8a53c064b09d7103867f74e67b542c5aba8d0dc5 100644 (file)
@@ -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,
index dae90ca3ed3a997218be727791d5600baa64072f..6ae1734a5a35a1a22e37eb2edbc60ffbd85eca6c 100644 (file)
@@ -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']
   )
 }
index f9bfcf6ebb9b3c1aada8f2887096fbbd9ebada1c..aae2b782690d715cc806c75af1b2db86b5af321f 100644 (file)
@@ -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
index 1519c4f858c17a3b791828a8af5c8c69bd7574c4..a5f4a1d3e9247984eefa1595b45338259334ff73 100644 (file)
@@ -1,4 +1,5 @@
 import { Token, TokenType } from './pathTokenizer'
+import { assign } from '../utils'
 
 export type PathParams = Record<string, string | string[]>
 
@@ -108,10 +109,7 @@ export function tokensToParser(
   segments: Array<Token[]>,
   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<number[]> = []
index 1358974bee1bb62797e46d85fc1504dc5dc9866d..e8a56e614852453123001c9cf8b88e123228f4f0 100644 (file)
@@ -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<RouteLocationRaw, string> | 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)
     }
 
index de75374bec573e32306203d7e768888fd009bc10..ee3385cb8f70fede112388a4d0e03240d8a19e5a 100644 (file)
@@ -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