]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(errors): fix error inheritance in IE<10
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 5 Jul 2019 18:21:16 +0000 (20:21 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 5 Jul 2019 18:21:16 +0000 (20:21 +0200)
__tests__/matcher.spec.js
explorations/tsconfig.json
package.json
rollup.config.js
src/consola.ts
src/errors.ts
src/history/html5.ts
src/index.ts
src/router.ts
yarn.lock

index 3fb513592990310ff3eaf2560bb927cc0a2f65c8..3f5fd9b3dc9de7215aae7072e8eef6b21186dc09 100644 (file)
@@ -138,7 +138,7 @@ describe('Router Matcher', () => {
         expect(
           assertErrorMatch({ path: '/', component }, { path: '/foo' })
         ).toMatchInlineSnapshot(
-          `[Error: No match for {"path":"/foo","params":{},"query":{},"hash":"","fullPath":"/"}]`
+          `[NoRouteMatchError: No match for {"path":"/foo","params":{},"query":{},"hash":"","fullPath":"/"}]`
         )
       })
     })
@@ -164,7 +164,7 @@ describe('Router Matcher', () => {
         expect(
           assertErrorMatch({ path: '/', component }, { name: 'Home' })
         ).toMatchInlineSnapshot(
-          `[Error: No match for {"path":"/","name":"Home","params":{},"query":{},"hash":"","fullPath":"/"}]`
+          `[NoRouteMatchError: No match for {"path":"/","name":"Home","params":{},"query":{},"hash":"","fullPath":"/"}]`
         )
       })
     })
@@ -411,12 +411,12 @@ describe('Router Matcher', () => {
               { path: '/redirect', params: {}, matched: [], name: undefined }
             )
           ).toMatchInlineSnapshot(`
-            [Error: Cannot redirect using a relative location:
-            {
-              "params": {}
-            }
-            Use the function redirect and explicitely provide a name]
-          `)
+                        [InvalidRouteMatch: Cannot redirect using a relative location:
+                        {
+                          "params": {}
+                        }
+                        Use the function redirect and explicitely provide a name]
+                    `)
         })
 
         it('normalize a location when redirecting', () => {
@@ -466,7 +466,7 @@ describe('Router Matcher', () => {
             { ...start, matched: start.matched.map(normalizeRouteRecord) }
           )
         ).toMatchInlineSnapshot(
-          `[Error: No match for {"name":"home","params":{},"path":"/"}]`
+          `[NoRouteMatchError: No match for {"name":"home","params":{},"path":"/"}]`
         )
       })
     })
index 0a690fa2e6544690736ffc841ec5c8da10e33c91..85d589435c46e485b23f08fe3ed677dca7eb0575 100644 (file)
@@ -1,7 +1,7 @@
 {
   "include": ["./**/*.ts"],
   "compilerOptions": {
-    "target": "es3" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
+    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
     "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
     // "lib": ["es2017.object"] /* Specify library files to be included in the compilation. */,
     // "declaration": true /* Generates corresponding '.d.ts' file. */,
index 86d78aa8194a2880146bbc5f4a899c3f225b1084..619e0530257a7d6f662ce5764516e41113a28ae6 100644 (file)
     "jsdom": "^15.1.1",
     "mocha": "^6.1.3",
     "prettier": "^1.18.2",
-    "rollup": "^1.16.3",
+    "rollup": "^1.16.6",
     "rollup-plugin-alias": "^1.5.2",
     "rollup-plugin-commonjs": "^10.0.1",
     "rollup-plugin-node-resolve": "^5.2.0",
     "rollup-plugin-replace": "^2.2.0",
-    "rollup-plugin-terser": "^5.0.0",
+    "rollup-plugin-terser": "^5.1.0",
     "rollup-plugin-typescript2": "^0.21.2",
     "ts-jest": "^24.0.2",
     "ts-loader": "^6.0.4",
index 39d331675ad817453e7d006763c59d38a8625bab..f771eca354c2786f029448092c901c51f9de49bd 100644 (file)
@@ -77,7 +77,7 @@ function createEntry(
           // same for d.ts files
           declaration: format === 'es' && isBrowser && !minify,
           module: 'esnext', // we need to override it because mocha requires this value to be commonjs
-          target: format === 'iife' || format === 'cjs' ? 'es3' : 'esnext',
+          target: format === 'iife' || format === 'cjs' ? 'es5' : 'esnext',
         },
       },
     })
index 0929e402516efb600fd76d64ccd3b5c3720e0e3d..9c884a896f51f151cf9940dd6744d56ae1fa374d 100644 (file)
@@ -1,4 +1,8 @@
 // Own simplified consola version
+// Consola doesn't work on IE9
+
+type LogCommand = 'info' | 'log' | 'error' | 'warn'
+const logs: LogCommand[] = ['info', 'log', 'error', 'warn']
 
 export default {
   tag: '',
@@ -12,4 +16,11 @@ export default {
     if (this.tag) console.info(`[${this.tag}]`, ...args)
     else console.info(...args)
   },
+
+  mockTypes(spyCreator: () => any): void {
+    for (const log of logs) {
+      // @ts-ignore
+      this[log] = spyCreator()
+    }
+  },
 }
index df49b9d66764806825b674691b8736448c1d419b..286735831fb79e3e4dddfb1829c9590ae6949393 100644 (file)
@@ -1,16 +1,80 @@
 import { RouteLocationNormalized, RouteLocation } from './types'
 
-export class NoRouteMatchError extends Error {
+// we could use symbols, but this is for IE9 only and there is
+// not Symbol support anyway
+const isRouterError = '__RouterError'
+
+/**
+ * Generic Error coming from the Router.
+ */
+export class RouterError extends Error {
+  protected __proto__: any
+  // @ts-ignore for IE inheritance support
+  private [isRouterError] = true
+
+  /**
+   * Creates a Router specific Error
+   *
+   * @param message Error Message
+   */
+  constructor(message: string) {
+    super(message)
+
+    // restore prototype chain
+    const actualProto = new.target.prototype
+
+    if (Object.setPrototypeOf) {
+      Object.setPrototypeOf(this, actualProto)
+    } else {
+      this.__proto__ = actualProto
+    }
+  }
+
+  static is(error: Error): error is RouterError {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isRouterError]
+    } else {
+      return error instanceof RouterError
+    }
+  }
+
+  get name() {
+    return this.constructor.name
+  }
+}
+
+const isNoRouteMatchError = '__NoRouteMatchError'
+export class NoRouteMatchError extends RouterError {
+  // @ts-ignore for IE inheritance support
+  private [isNoRouteMatchError] = true
+
   constructor(currentLocation: any, location: any) {
     super('No match for ' + JSON.stringify({ ...currentLocation, ...location }))
-    Object.setPrototypeOf(this, new.target.prototype)
+  }
+
+  static is(error: Error): error is NoRouteMatchError {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isNoRouteMatchError]
+    } else {
+      return error instanceof NoRouteMatchError
+    }
   }
 }
 
+const isInvalidRouteMatch = '__InvalidRouteMatch'
 /**
  * Error used when the matcher fails to resolve a location
  */
-export class InvalidRouteMatch extends Error {
+export class InvalidRouteMatch extends RouterError {
+  // @ts-ignore for IE inheritance support
+  private [isNoRouteMatchError] = true
+
   constructor(location: any) {
     // TODO: improve the error to include currentLocation and use it for more cases
     super(
@@ -18,15 +82,29 @@ export class InvalidRouteMatch extends Error {
         location
       )}\nUse the function redirect and explicitely provide a name`
     )
-    Object.setPrototypeOf(this, new.target.prototype)
+  }
+
+  static is(error: Error): error is InvalidRouteMatch {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isInvalidRouteMatch]
+    } else {
+      return error instanceof InvalidRouteMatch
+    }
   }
 }
 
+const isNavigationGuardRedirect = '__NavigationGuardRedirect'
 /**
  * Error used when rejecting a navigation because of a redirection. Contains
  * information about where we where trying to go and where we are going instead
  */
-export class NavigationGuardRedirect extends Error {
+export class NavigationGuardRedirect extends RouterError {
+  // @ts-ignore for IE inheritance support
+  private [isNoRouteMatchError] = true
+
   to: RouteLocation
   from: RouteLocationNormalized
   // TODO: refactor order of argumnets
@@ -37,50 +115,85 @@ export class NavigationGuardRedirect extends Error {
         to
       )}" via a navigation guard`
     )
-    Object.setPrototypeOf(this, new.target.prototype)
 
     this.from = from
     this.to = to
   }
+
+  static is(error: Error): error is NavigationGuardRedirect {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isNavigationGuardRedirect]
+    } else {
+      return error instanceof NavigationGuardRedirect
+    }
+  }
 }
 
+const isNavigationAborted = '__NavigationAborted'
 /**
  * Navigation aborted by next(false)
  */
-export class NavigationAborted extends Error {
+export class NavigationAborted extends RouterError {
+  // @ts-ignore for IE inheritance support
+  private [isNavigationAborted] = true
+
   to: RouteLocationNormalized
   from: RouteLocationNormalized
   constructor(to: RouteLocationNormalized, from: RouteLocationNormalized) {
     super(
-      `Navigation aborted from "${from.fullPath}" to "${
-        to.fullPath
-      }" via a navigation guard`
+      `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard`
     )
-    Object.setPrototypeOf(this, new.target.prototype)
 
     this.from = from
     this.to = to
   }
+
+  static is(error: Error): error is NavigationAborted {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isNavigationAborted]
+    } else {
+      return error instanceof NavigationAborted
+    }
+  }
 }
 
+const isNavigationCancelled = '__NavigationCancelled'
 /**
  * Navigation canceled by the user by pushing/replacing a new location
  * TODO: is the name good?
  */
-export class NavigationCancelled extends Error {
+// @ts-ignore RouterError is a constructor
+export class NavigationCancelled extends RouterError {
+  // @ts-ignore for IE inheritance support
+  private [isNavigationCancelled] = true
+
   to: RouteLocationNormalized
   from: RouteLocationNormalized
   constructor(to: RouteLocationNormalized, from: RouteLocationNormalized) {
     super(
-      `Navigation cancelled from "${from.fullPath}" to "${
-        to.fullPath
-      }" with a new \`push\` or \`replace\``
+      `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new \`push\` or \`replace\``
     )
-    Object.setPrototypeOf(this, new.target.prototype)
 
     this.from = from
     this.to = to
   }
+
+  static is(error: Error): error is NavigationCancelled {
+    // only IE9 seems to break the inheritance chain
+    // and set Error as the name
+    if (error.name === 'Error') {
+      // @ts-ignore for IE inheritance suport
+      return error[isNavigationCancelled]
+    } else {
+      return error instanceof NavigationCancelled
+    }
+  }
 }
 
 function stringifyRoute(to: RouteLocation): string {
index 6933bad6624e425e2570e4e6f214cafd41027aff..0fb5a702cdbc13d33e10037371e7db0ff53a24d9 100644 (file)
@@ -1,4 +1,4 @@
-import consola from 'consola'
+import consola from '../consola'
 import { BaseHistory, HistoryLocationNormalized, HistoryLocation } from './base'
 import { NavigationCallback, HistoryState, NavigationDirection } from './base'
 
index bfc1bc0ce7e856c073b57224592909d29be05f60..11a399bb4e40a971e789f30f6fcb37990d27e5e8 100644 (file)
@@ -1,5 +1,8 @@
 import { Router, RouterOptions } from './router'
 import { HTML5History } from './history/html5'
+import { HashHistory } from './history/hash'
+import { AbstractHistory } from './history/abstract'
+import { BaseHistory } from './history/base'
 import { PluginFunction, VueConstructor } from 'vue'
 import View from './components/View'
 import Link from './components/Link'
@@ -60,7 +63,14 @@ const plugin: PluginFunction<void> = Vue => {
     strats.created
 }
 
-export { Router, HTML5History, plugin }
+export {
+  Router,
+  HTML5History,
+  HashHistory,
+  AbstractHistory,
+  BaseHistory,
+  plugin,
+}
 
 export default class VueRouter extends Router {
   static install = plugin
index 68877714af3613fb8abd4445dd6233e51dde1e8b..f6c69ba5290fb4c9e59726e4c991cc036971fc64 100644 (file)
@@ -78,7 +78,7 @@ export class Router {
         }
         this.updateReactiveRoute()
       } catch (error) {
-        if (error instanceof NavigationGuardRedirect) {
+        if (NavigationGuardRedirect.is(error)) {
           // TODO: refactor the duplication of new NavigationCancelled by
           // checking instanceof NavigationError (it's another TODO)
           // a more recent navigation took place
@@ -93,7 +93,7 @@ export class Router {
           // the error is already handled by router.push
           // we just want to avoid logging the error
           this.push(error.to).catch(() => {})
-        } else if (error instanceof NavigationAborted) {
+        } else if (NavigationAborted.is(error)) {
           // TODO: test on different browsers ensure consistent behavior
           // TODO: this doesn't work if the user directly calls window.history.go(-n) with n > 1
           // We can override the go method to retrieve the number but not sure if all browsers allow that
@@ -234,7 +234,7 @@ export class Router {
     try {
       await this.navigate(toLocation, this.currentRoute)
     } catch (error) {
-      if (error instanceof NavigationGuardRedirect) {
+      if (NavigationGuardRedirect.is(error)) {
         // push was called while waiting in guards
         if (this.pendingLocation !== toLocation) {
           // TODO: trigger onError as well
@@ -454,7 +454,7 @@ export class Router {
     try {
       await this.navigate(toLocation, this.currentRoute)
     } catch (error) {
-      if (error instanceof NavigationGuardRedirect) {
+      if (NavigationGuardRedirect.is(error)) {
         // push was called while waiting in guards
         if (this.pendingLocation !== toLocation) {
           // TODO: trigger onError as well
index b01e5c3f3b9efe92eae9cde3af9613d55ef82b2a..afce9923115f0d3f1fce17e8f585cf8389f794a6 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1"
   integrity sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng==
 
-"@types/node@^12.0.8":
-  version "12.0.10"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.10.tgz#51babf9c7deadd5343620055fc8aff7995c8b031"
-  integrity sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ==
+"@types/node@^12.0.10":
+  version "12.0.12"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.12.tgz#cc791b402360db1eaf7176479072f91ee6c6c7ca"
+  integrity sha512-Uy0PN4R5vgBUXFoJrKryf5aTk3kJ8Rv3PdlHjl6UaX+Cqp1QE0yPQ68MPXGrZOfG7gZVNDIJZYyot0B9ubXUrQ==
 
 "@types/resolve@0.0.8":
   version "0.0.8"
@@ -5313,13 +5313,14 @@ rollup-plugin-replace@^2.2.0:
     magic-string "^0.25.2"
     rollup-pluginutils "^2.6.0"
 
-rollup-plugin-terser@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.0.0.tgz#ac50fdb703b580447a7e6b1692aeed515a6be8cf"
-  integrity sha512-W+jJ4opYnlmNyVW0vtRufs+EGf68BIJ7bnOazgz8mgz8pA9lUyrEifAhPs5y9M16wFeAyBGaRjKip4dnFBtXaw==
+rollup-plugin-terser@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.1.0.tgz#82e637613d5af6c85ac4319b508b1c065d64075c"
+  integrity sha512-tvJweguo+f9T1SBcSmEMaArCUM07mIg61ArqPj3Fty9OdwTLCxBUBdxS3e1cU68Z1lXf52JBhbt3yRuQoHLtQg==
   dependencies:
     "@babel/code-frame" "^7.0.0"
     jest-worker "^24.6.0"
+    rollup-pluginutils "^2.8.1"
     serialize-javascript "^1.7.0"
     terser "^4.0.0"
 
@@ -5348,13 +5349,13 @@ rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1:
   dependencies:
     estree-walker "^0.6.1"
 
-rollup@^1.16.3:
-  version "1.16.3"
-  resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.16.3.tgz#9b8bcf31523efc83447a9624bd2fe6ca58caa1e7"
-  integrity sha512-iXINUUEk2NTZXE3GcUtLQt2cvfQsAUXBQ8AFsDK8tg7Wp5bwTKdZXPdzB2IJQwHpdUNfsIgYMAfajurh7SVTnA==
+rollup@^1.16.6:
+  version "1.16.6"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.16.6.tgz#0d979e3edd14a27e5436a145c7f1fcef35e0083b"
+  integrity sha512-oM3iKkzPCq9Da95wCnNfS8YlNZjgCD5c/TceKnJIthI9FOeJqnO3PUr/C5Suv9Kjzh0iphKL02PLeja3A5AMIA==
   dependencies:
     "@types/estree" "0.0.39"
-    "@types/node" "^12.0.8"
+    "@types/node" "^12.0.10"
     acorn "^6.1.1"
 
 rsvp@^4.8.4: