Repository: kyeotic/raviger
Pull request #95 (2021-07-16 20:48:05)

handle <redirect> in useRoutes
closes #84

This is hacky, but it works.

The root of the issue is that `<Redirect />` causes a change to the path state _after_ `useRoutes` has returned, but before the render loop has finished. `usePath` and `useLocationChange` both fire, but React appears to suppress the re-render that this should cause, even though its from a `useLayoutEffect`. I'm sure there is a better way to do this, but I don't know how.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff8b819..b1fbc17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2.4.2] - 2021-07-16
+### Fixed
+- `useRoutes` not updating after an internal `<Redirect />` updates the location
+
 ## [2.4.1] - 2021-06-28
 ### Changed
 - Rollup build to `keep_fnames` to retain component name checking
diff --git a/example/src/App2.js b/example/src/App2.js
index c682332..6071d13 100644
--- a/example/src/App2.js
+++ b/example/src/App2.js
@@ -1,73 +1,37 @@
 import React from 'react'
-import {
-  useRoutes,
-  Link,
-  usePath,
-  useQueryParams,
-  Redirect,
-  navigate
-} from '../../src/main.js'
+import { useRoutes, Redirect } from '../../src/main.js'
 
-const PROJECTS = [
-  'ac2fd99a-495a-498a-843c-b7ddd7f7bf3f',
-  'b986a8e3-3f92-4112-9cda-506a1ca28ef0'
-]
-const BrandingPage = ({ projectId }) => {
-  return React.createElement(
-    'h2',
-    null,
-    'BrandingPage content for project ',
-    projectId
-  )
+// import './styles.css'
+
+const Page = () => {
+  return <div>Hi from /bar/foo</div>
 }
-function ProjectArea({ projectId }) {
-  const match = useRoutes(
-    React.useMemo(
-      () => ({
-        '/settings/branding': () =>
-          React.createElement(BrandingPage, { projectId: projectId })
-      }),
-      [projectId]
-    ),
-    {
-      basePath: `/projects/${projectId}`
-    }
-  )
-  return React.createElement(
-    'div',
-    { style: { display: 'flex', flexDirection: 'column' } },
-    React.createElement('h1', null, 'Raviger issue'),
-    React.createElement(
-      'select',
-      {
-        onChange: React.useCallback(e => {
-          navigate(`/projects/${e.target.value}/settings/branding`)
-        }, []),
-        value: projectId
-      },
-      React.createElement('option', { value: PROJECTS[0] }, PROJECTS[0]),
-      React.createElement('option', { value: PROJECTS[1] }, PROJECTS[1])
-    ),
-    match !== null && match !== void 0
-      ? match
-      : React.createElement('h2', null, '404')
-  )
+
+const routes = {
+  '/bar': () => <Redirect to={'/bar/foo'} />,
+  '/bar/foo': () => <Page />,
+  '*': () => <Redirect to={'/bar/foo'} />
 }
+
 export default function App() {
-  const match = useRoutes(
-    React.useMemo(
-      () => ({
-        '/projects/:projectId*': ({ projectId }) =>
-          React.createElement(ProjectArea, { projectId: projectId })
-      }),
-      []
-    )
+  return (
+    <div>
+      If it works as expected it should say <b>Hi from /bar/foo</b> below the
+      buttons:
+      <br />
+      <button onClick={() => (window.location.href = '/')}>
+        Retry from / (doesn't render Page)
+      </button>
+      <br />
+      <button onClick={() => (window.location.href = '/bar')}>
+        Retry from /bar (doesn't render Page)
+      </button>
+      <br />
+      <button onClick={() => (window.location.href = '/bar/foo')}>
+        Retry from /bar/foo (works)
+      </button>
+      <br />
+      {useRoutes(routes)}
+    </div>
   )
-  React.useLayoutEffect(() => {
-    const index = Math.round(Math.random()) // the initial projectId doesn't matter
-    navigate(`/projects/${PROJECTS[index]}/settings/branding`)
-  }, [])
-  return match !== null && match !== void 0
-    ? match
-    : React.createElement('h2', null, '404')
 }
diff --git a/example/src/index.js b/example/src/index.js
index 0af7af3..8493e69 100644
--- a/example/src/index.js
+++ b/example/src/index.js
@@ -1,7 +1,7 @@
 import React from 'react'
 import ReactDOM from 'react-dom'
-import App from './App'
-// import App from './App2'
+// import App from './App'
+import App from './App2'
 // import App from './NestedApp.js'
 // eslint-disable-next-line import/no-unresolved
 // import whyDidYouRender from 'whyDidYouRender'
diff --git a/package-lock.json b/package-lock.json
index 01be4f2..bb7d341 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8251,6 +8251,8 @@
     },
     "node_modules/fsevents/node_modules/abbrev": {
       "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8258,6 +8260,8 @@
     },
     "node_modules/fsevents/node_modules/ansi-regex": {
       "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8268,6 +8272,8 @@
     },
     "node_modules/fsevents/node_modules/aproba": {
       "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8275,6 +8281,8 @@
     },
     "node_modules/fsevents/node_modules/are-we-there-yet": {
       "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8286,6 +8294,8 @@
     },
     "node_modules/fsevents/node_modules/balanced-match": {
       "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8293,6 +8303,8 @@
     },
     "node_modules/fsevents/node_modules/brace-expansion": {
       "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8304,6 +8316,8 @@
     },
     "node_modules/fsevents/node_modules/chownr": {
       "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+      "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8311,6 +8325,8 @@
     },
     "node_modules/fsevents/node_modules/code-point-at": {
       "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8321,6 +8337,8 @@
     },
     "node_modules/fsevents/node_modules/concat-map": {
       "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8328,6 +8346,8 @@
     },
     "node_modules/fsevents/node_modules/console-control-strings": {
       "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8335,6 +8355,8 @@
     },
     "node_modules/fsevents/node_modules/core-util-is": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8342,6 +8364,8 @@
     },
     "node_modules/fsevents/node_modules/debug": {
       "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8352,6 +8376,8 @@
     },
     "node_modules/fsevents/node_modules/deep-extend": {
       "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8362,6 +8388,8 @@
     },
     "node_modules/fsevents/node_modules/delegates": {
       "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8369,6 +8397,8 @@
     },
     "node_modules/fsevents/node_modules/detect-libc": {
       "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
       "dev": true,
       "inBundle": true,
       "license": "Apache-2.0",
@@ -8382,6 +8412,8 @@
     },
     "node_modules/fsevents/node_modules/fs-minipass": {
       "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
+      "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8392,6 +8424,8 @@
     },
     "node_modules/fsevents/node_modules/fs.realpath": {
       "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8399,6 +8433,8 @@
     },
     "node_modules/fsevents/node_modules/gauge": {
       "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8416,6 +8452,8 @@
     },
     "node_modules/fsevents/node_modules/glob": {
       "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8434,6 +8472,8 @@
     },
     "node_modules/fsevents/node_modules/has-unicode": {
       "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8441,6 +8481,8 @@
     },
     "node_modules/fsevents/node_modules/iconv-lite": {
       "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8454,6 +8496,8 @@
     },
     "node_modules/fsevents/node_modules/ignore-walk": {
       "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+      "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8464,6 +8508,8 @@
     },
     "node_modules/fsevents/node_modules/inflight": {
       "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8475,6 +8521,8 @@
     },
     "node_modules/fsevents/node_modules/inherits": {
       "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8482,6 +8530,8 @@
     },
     "node_modules/fsevents/node_modules/ini": {
       "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8492,6 +8542,8 @@
     },
     "node_modules/fsevents/node_modules/is-fullwidth-code-point": {
       "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8505,6 +8557,8 @@
     },
     "node_modules/fsevents/node_modules/isarray": {
       "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8512,6 +8566,8 @@
     },
     "node_modules/fsevents/node_modules/minimatch": {
       "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8525,6 +8581,8 @@
     },
     "node_modules/fsevents/node_modules/minimist": {
       "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8532,6 +8590,8 @@
     },
     "node_modules/fsevents/node_modules/minipass": {
       "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+      "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8543,6 +8603,8 @@
     },
     "node_modules/fsevents/node_modules/minizlib": {
       "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+      "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8553,6 +8615,8 @@
     },
     "node_modules/fsevents/node_modules/mkdirp": {
       "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8566,6 +8630,8 @@
     },
     "node_modules/fsevents/node_modules/ms": {
       "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8573,6 +8639,8 @@
     },
     "node_modules/fsevents/node_modules/needle": {
       "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz",
+      "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8591,6 +8659,8 @@
     },
     "node_modules/fsevents/node_modules/node-pre-gyp": {
       "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz",
+      "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
       "dev": true,
       "inBundle": true,
       "license": "BSD-3-Clause",
@@ -8613,6 +8683,8 @@
     },
     "node_modules/fsevents/node_modules/nopt": {
       "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+      "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8627,6 +8699,8 @@
     },
     "node_modules/fsevents/node_modules/npm-bundled": {
       "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8634,6 +8708,8 @@
     },
     "node_modules/fsevents/node_modules/npm-packlist": {
       "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz",
+      "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8645,6 +8721,8 @@
     },
     "node_modules/fsevents/node_modules/npmlog": {
       "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8658,6 +8736,8 @@
     },
     "node_modules/fsevents/node_modules/number-is-nan": {
       "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8668,6 +8748,8 @@
     },
     "node_modules/fsevents/node_modules/object-assign": {
       "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8678,6 +8760,8 @@
     },
     "node_modules/fsevents/node_modules/once": {
       "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8688,6 +8772,8 @@
     },
     "node_modules/fsevents/node_modules/os-homedir": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8698,6 +8784,8 @@
     },
     "node_modules/fsevents/node_modules/os-tmpdir": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8708,6 +8796,8 @@
     },
     "node_modules/fsevents/node_modules/osenv": {
       "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8719,6 +8809,8 @@
     },
     "node_modules/fsevents/node_modules/path-is-absolute": {
       "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8729,6 +8821,8 @@
     },
     "node_modules/fsevents/node_modules/process-nextick-args": {
       "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8736,6 +8830,8 @@
     },
     "node_modules/fsevents/node_modules/rc": {
       "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
       "dev": true,
       "inBundle": true,
       "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
@@ -8752,6 +8848,8 @@
     },
     "node_modules/fsevents/node_modules/rc/node_modules/minimist": {
       "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8759,6 +8857,8 @@
     },
     "node_modules/fsevents/node_modules/readable-stream": {
       "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8775,6 +8875,8 @@
     },
     "node_modules/fsevents/node_modules/rimraf": {
       "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8788,6 +8890,8 @@
     },
     "node_modules/fsevents/node_modules/safe-buffer": {
       "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8795,6 +8899,8 @@
     },
     "node_modules/fsevents/node_modules/safer-buffer": {
       "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8802,6 +8908,8 @@
     },
     "node_modules/fsevents/node_modules/sax": {
       "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8809,6 +8917,8 @@
     },
     "node_modules/fsevents/node_modules/semver": {
       "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+      "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8819,6 +8929,8 @@
     },
     "node_modules/fsevents/node_modules/set-blocking": {
       "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8826,6 +8938,8 @@
     },
     "node_modules/fsevents/node_modules/signal-exit": {
       "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8833,6 +8947,8 @@
     },
     "node_modules/fsevents/node_modules/string_decoder": {
       "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8843,6 +8959,8 @@
     },
     "node_modules/fsevents/node_modules/string-width": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8858,6 +8976,8 @@
     },
     "node_modules/fsevents/node_modules/strip-ansi": {
       "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8871,6 +8991,8 @@
     },
     "node_modules/fsevents/node_modules/strip-json-comments": {
       "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8881,6 +9003,8 @@
     },
     "node_modules/fsevents/node_modules/tar": {
       "version": "4.4.8",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
+      "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8900,6 +9024,8 @@
     },
     "node_modules/fsevents/node_modules/util-deprecate": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -8907,6 +9033,8 @@
     },
     "node_modules/fsevents/node_modules/wide-align": {
       "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8917,6 +9045,8 @@
     },
     "node_modules/fsevents/node_modules/wrappy": {
       "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -8924,6 +9054,8 @@
     },
     "node_modules/fsevents/node_modules/yallist": {
       "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+      "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -26898,24 +27030,32 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+          "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "ansi-regex": {
           "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "aproba": {
           "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "are-we-there-yet": {
           "version": "1.1.5",
+          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+          "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -26926,12 +27066,16 @@
         },
         "balanced-match": {
           "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -26942,36 +27086,48 @@
         },
         "chownr": {
           "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+          "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "code-point-at": {
           "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "debug": {
           "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -26981,24 +27137,32 @@
         },
         "deep-extend": {
           "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+          "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "delegates": {
           "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "detect-libc": {
           "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+          "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "fs-minipass": {
           "version": "1.2.5",
+          "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
+          "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27008,12 +27172,16 @@
         },
         "fs.realpath": {
           "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "gauge": {
           "version": "2.7.4",
+          "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27030,6 +27198,8 @@
         },
         "glob": {
           "version": "7.1.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27044,12 +27214,16 @@
         },
         "has-unicode": {
           "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "iconv-lite": {
           "version": "0.4.24",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27059,6 +27233,8 @@
         },
         "ignore-walk": {
           "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+          "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27068,6 +27244,8 @@
         },
         "inflight": {
           "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27078,18 +27256,24 @@
         },
         "inherits": {
           "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "ini": {
           "version": "1.3.5",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27099,12 +27283,16 @@
         },
         "isarray": {
           "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "minimatch": {
           "version": "3.0.4",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27114,12 +27302,16 @@
         },
         "minimist": {
           "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "minipass": {
           "version": "2.3.5",
+          "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+          "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27130,6 +27322,8 @@
         },
         "minizlib": {
           "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+          "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27139,6 +27333,8 @@
         },
         "mkdirp": {
           "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27148,12 +27344,16 @@
         },
         "ms": {
           "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "needle": {
           "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz",
+          "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27165,6 +27365,8 @@
         },
         "node-pre-gyp": {
           "version": "0.12.0",
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz",
+          "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27183,6 +27385,8 @@
         },
         "nopt": {
           "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27193,12 +27397,16 @@
         },
         "npm-bundled": {
           "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+          "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "npm-packlist": {
           "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz",
+          "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27209,6 +27417,8 @@
         },
         "npmlog": {
           "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27221,18 +27431,24 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "once": {
           "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27242,18 +27458,24 @@
         },
         "os-homedir": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "os-tmpdir": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "osenv": {
           "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+          "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27264,18 +27486,24 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "process-nextick-args": {
           "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "rc": {
           "version": "1.2.8",
+          "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+          "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27288,6 +27516,8 @@
           "dependencies": {
             "minimist": {
               "version": "1.2.0",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
               "bundled": true,
               "dev": true,
               "optional": true
@@ -27296,6 +27526,8 @@
         },
         "readable-stream": {
           "version": "2.3.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27311,6 +27543,8 @@
         },
         "rimraf": {
           "version": "2.6.3",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+          "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27320,42 +27554,56 @@
         },
         "safe-buffer": {
           "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+          "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "sax": {
           "version": "1.2.4",
+          "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "semver": {
           "version": "5.7.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+          "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "set-blocking": {
           "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "signal-exit": {
           "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "string_decoder": {
           "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27365,6 +27613,8 @@
         },
         "string-width": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27376,6 +27626,8 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27385,12 +27637,16 @@
         },
         "strip-json-comments": {
           "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "tar": {
           "version": "4.4.8",
+          "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
+          "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27406,12 +27662,16 @@
         },
         "util-deprecate": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "wide-align": {
           "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+          "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
           "bundled": true,
           "dev": true,
           "optional": true,
@@ -27421,12 +27681,16 @@
         },
         "wrappy": {
           "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "yallist": {
           "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
           "bundled": true,
           "dev": true,
           "optional": true
diff --git a/src/path.js b/src/path.js
index f6d2211..86af3d3 100644
--- a/src/path.js
+++ b/src/path.js
@@ -17,7 +17,10 @@ export function usePath(basePath) {
   basePath = basePath || contextBasePath
 
   const [path, setPath] = useState(getFormattedPath(basePath))
-  useLocationChange(setPath, { basePath, inheritBasePath: !basePath })
+  useLocationChange(newPath => setPath(newPath), {
+    basePath,
+    inheritBasePath: !basePath
+  })
 
   return contextPath || path
 }
@@ -84,7 +87,6 @@ export function useLocationChange(
     // No predicate defaults true
     if (isActive !== undefined && !isPredicateActive(isActive)) return
     if (shouldCancelNavigation()) return
-    // console.log('loc', basePath || 'none', getFormattedPath(basePath))
     setRef.current(getFormattedPath(basePath))
   }, [isActive, basePath])
 
diff --git a/src/redirect.js b/src/redirect.js
index 3e1bf23..1e9cdd4 100644
--- a/src/redirect.js
+++ b/src/redirect.js
@@ -33,5 +33,5 @@ export function useRedirect(
     if (currentPath === predicateUrl) {
       navigate(url, undefined, replace)
     }
-  }, [predicateUrl, url, undefined, replace, currentPath])
+  }, [predicateUrl, url, replace, currentPath])
 }
diff --git a/src/router.js b/src/router.js
index 50c314f..5144b4f 100644
--- a/src/router.js
+++ b/src/router.js
@@ -1,4 +1,4 @@
-import React, { useMemo } from 'react'
+import React, { useMemo, useState, useLayoutEffect } from 'react'
 import { BasePathContext, PathContext } from './context.js'
 import { isNode, setSsrPath, getSsrPath } from './node'
 import { usePath, getFormattedPath } from './path.js'
@@ -21,9 +21,11 @@ export function useRoutes(
     If usePath returns latest it causes render thrashing
     If useRoutes hacks itself into the latest path nothing bad happens (...afaik)
   */
-
   const path = usePath(basePath) && getFormattedPath(basePath)
 
+  // Handle potential <Redirect /> use in routes
+  useRedirectDetection(basePath, path)
+
   // Get the current route
   const route = matchRoute(routes, path, {
     routeProps,
@@ -113,3 +115,17 @@ function hashRoutes(routes) {
   const paths = Object.keys(routes).sort()
   return paths.join(':')
 }
+
+// React appears to suppress parent's re-rendering when a child's
+// useLayoutEffect updates internal state
+// the `navigate` call in useRedirect *does* cause usePath/useLocationChange
+// to fire, but without this hack useRoutes suppresses the update
+// TODO: find a better way to cause a synchronous update from useRoutes
+function useRedirectDetection(basePath, path) {
+  const [, forceRender] = useState(false)
+  useLayoutEffect(() => {
+    if (path !== getFormattedPath(basePath)) {
+      forceRender(s => !s)
+    }
+  }, [basePath, path])
+}


Test patch:
diff --git a/test/redirect.spec.js b/test/redirect.spec.js
index 467031c..0301021 100644
--- a/test/redirect.spec.js
+++ b/test/redirect.spec.js
@@ -1,6 +1,6 @@
 import React from 'react'
 import { render, act } from '@testing-library/react'
-import { useRedirect, navigate, Redirect } from '../src/main.js'
+import { useRedirect, navigate, Redirect, useRoutes } from '../src/main.js'
 
 beforeEach(() => {
   act(() => navigate('/'))
@@ -74,4 +74,39 @@ describe('Redirect', () => {
       'http://localhost/foo?stuff=junk'
     )
   })
+  test('redirects from useRoutes', async () => {
+    const Page = () => {
+      return <span data-testid="label">pass</span>
+    }
+    const routes = {
+      '/redirect': () => <Redirect to={'/real/page'} />,
+      '/real/page': () => <Page />,
+      '*': () => <Redirect to={'/real/page'} />
+    }
+
+    function Harness() {
+      const route = useRoutes(routes) || (
+        <span data-testid="label">not found</span>
+      )
+      return route
+    }
+
+    act(() => navigate('/'))
+    const { getByTestId } = render(<Harness />)
+
+    // Home
+    expect(getByTestId('label')).toHaveTextContent('pass')
+
+    // real
+    act(() => navigate('/real/page'))
+    expect(getByTestId('label')).toHaveTextContent('pass')
+
+    // redirect
+    act(() => navigate('/redirect'))
+    expect(getByTestId('label')).toHaveTextContent('pass')
+
+    // catch all redirect
+    act(() => navigate('/fake'))
+    expect(getByTestId('label')).toHaveTextContent('pass')
+  })
 })