Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace page loader with new route loader #19006

Merged
merged 34 commits into from
Nov 11, 2020

Conversation

Timer
Copy link
Member

@Timer Timer commented Nov 10, 2020

This pull request completely replaces our old page loader with a brand new route loader.

Our existing comprehensive test suite means I did not need to add a bunch of tests. I did add them where behavior was added or fixed.

Summary of the changes:

  • Eagerly evaluates prefetched pages in browser idle time (speeds up transitions)
  • Router is no longer frozen indefinitely if the Build Manifest never arrives
  • Router is no longer frozen indefinitely if a page fails to bootstrap
  • New withFuture utility instead of ad-hoc deduping per resource
  • Prefetching is now delayed until browser idle time to not impact TTI
  • Browsers without prefetch now fall back to eager evaluation instead of using preload
  • We're now ready to serve non-static assets with no-store without breaking prefetching
  • Application can now hydrate without fetching CSS assets—this is a huge performance win that was previously blocking hydration

The minor size increase here is unfortunate, but we have to incur it for correctness.


Fixes #18389
Fixes #18642

@vercel vercel bot temporarily deployed to Preview November 10, 2020 07:01 Inactive
@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 07:26 Inactive
@vercel vercel bot temporarily deployed to Preview November 10, 2020 07:42 Inactive
@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 15:03 Inactive
@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 16:20 Inactive
@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 17:15 Inactive
@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 18:41 Inactive
@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 20:06 Inactive
@vercel vercel bot temporarily deployed to Preview November 10, 2020 20:31 Inactive
@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 20:46 Inactive
@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@vercel vercel bot temporarily deployed to Preview November 10, 2020 22:04 Inactive
@vercel vercel bot temporarily deployed to Preview November 11, 2020 15:57 Inactive
@Timer Timer marked this pull request as ready for review November 11, 2020 16:04
@vercel vercel bot temporarily deployed to Preview November 11, 2020 16:04 Inactive
@ijjk
Copy link
Member

ijjk commented Nov 11, 2020

Stats from current PR

Default Server Mode (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
buildDuration 13.4s 13.6s ⚠️ +226ms
nodeModulesSize 84.8 MB 84.8 MB ⚠️ +5.05 kB
Page Load Tests Overall increase ✓
vercel/next.js canary Timer/next.js refactor/route-loader Change
/ failed reqs 0 0
/ total time (seconds) 2.489 2.406 -0.08
/ avg req/sec 1004.29 1039.01 +34.72
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.328 1.336 ⚠️ +0.01
/error-in-render avg req/sec 1883.06 1870.75 ⚠️ -12.31
Client Bundles (main, webpack, commons) Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..01e7.js gzip 11.3 kB 12.6 kB ⚠️ +1.34 kB
framework.HASH.js gzip 39 kB 39 kB
main-3e470be..d458.js gzip 7.48 kB N/A N/A
webpack-e067..f178.js gzip 751 B 751 B
main-8653720..d844.js gzip N/A 6.48 kB N/A
Overall change 58.5 kB 58.8 kB ⚠️ +347 B
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..dule.js gzip 7.05 kB 8.21 kB ⚠️ +1.16 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-0c7333c..dule.js gzip 6.47 kB 5.58 kB -882 B
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.2 kB 53.5 kB ⚠️ +275 B
Legacy Client Bundles (polyfills)
vercel/next.js canary Timer/next.js refactor/route-loader Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-0abe79e..7616.js gzip 1.55 kB 1.57 kB ⚠️ +20 B
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.96 kB 7.98 kB ⚠️ +20 B
Client Pages Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-d1cfb87..dule.js gzip 1.49 kB 1.51 kB ⚠️ +17 B
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.59 kB 5.6 kB ⚠️ +17 B
Client Build Manifests
vercel/next.js canary Timer/next.js refactor/route-loader Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes Overall decrease ✓
vercel/next.js canary Timer/next.js refactor/route-loader Change
index.html gzip 970 B 968 B -2 B
link.html gzip 977 B 976 B -1 B
withRouter.html gzip 963 B 962 B -1 B
Overall change 2.91 kB 2.91 kB -4 B

Diffs

Diff for _buildManifest.js
@@ -7,7 +7,7 @@ self.__BUILD_MANIFEST = {
   "/hooks": [
     "static\u002Fchunks\u002Fpages\u002Fhooks-880757f1d73d6b241fc3.js"
   ],
-  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-3125d338b922d5ab1776.js"],
+  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-4218f7a274c65b7f23dd.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-2e9bfd441bd88cd3382e.js"
   ],
Diff for _buildManifest.module.js
@@ -10,7 +10,7 @@ self.__BUILD_MANIFEST = {
     "static\u002Fchunks\u002Fpages\u002Fhooks-56fa58a6f0993d7d36d7.module.js"
   ],
   "/link": [
-    "static\u002Fchunks\u002Fpages\u002Flink-ec1fbf762c0209fe4b72.module.js"
+    "static\u002Fchunks\u002Fpages\u002Flink-cd84dab1f87965855b1c.module.js"
   ],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-368af3dfef3c9cd99dc3.module.js"
Diff for link-3125d33..2d5ab1776.js
@@ -302,11 +302,17 @@ _N_E = (window["webpackJsonp_N_E"] = window["webpackJsonp_N_E"] || []).push([
 
       var _slicedToArray = __webpack_require__("J4zp");
 
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
       exports.__esModule = true;
       exports.useIntersection = useIntersection;
 
       var _react = __webpack_require__("q1tI");
 
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      );
+
       var hasIntersectionObserver = typeof IntersectionObserver !== "undefined";
 
       function useIntersection(_ref) {
@@ -346,7 +352,10 @@ _N_E = (window["webpackJsonp_N_E"] = window["webpackJsonp_N_E"] || []).push([
         (0, _react.useEffect)(
           function() {
             if (!hasIntersectionObserver) {
-              if (!visible) setVisible(true);
+              if (!visible)
+                (0, _requestIdleCallback["default"])(function() {
+                  return setVisible(true);
+                });
             }
           },
           [visible]
Diff for link-ec1fbf7..72.module.js
@@ -277,11 +277,17 @@
     /***/ vNVm: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
       exports.__esModule = true;
       exports.useIntersection = useIntersection;
 
       var _react = __webpack_require__("q1tI");
 
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      );
+
       var hasIntersectionObserver = typeof IntersectionObserver !== "undefined";
 
       function useIntersection(_ref) {
@@ -312,7 +318,8 @@
         );
         (0, _react.useEffect)(() => {
           if (!hasIntersectionObserver) {
-            if (!visible) setVisible(true);
+            if (!visible)
+              (0, _requestIdleCallback.default)(() => setVisible(true));
           }
         }, [visible]);
         return [setRef, visible];
Diff for 677f882d2ed8..cf.module.js
@@ -55,6 +55,32 @@
       /***/
     },
 
+    /***/ "0G5g": /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      exports.__esModule = true;
+      exports.default = void 0;
+
+      var requestIdleCallback =
+        (typeof self !== "undefined" && self.requestIdleCallback) ||
+        function(cb) {
+          var start = Date.now();
+          return setTimeout(function() {
+            cb({
+              didTimeout: false,
+              timeRemaining: function timeRemaining() {
+                return Math.max(0, 50 - (Date.now() - start));
+              }
+            });
+          }, 1);
+        };
+
+      var _default = requestIdleCallback;
+      exports.default = _default;
+
+      /***/
+    },
+
     /***/ "284h": /***/ function(module, exports, __webpack_require__) {
       var _typeof = __webpack_require__("cDf5");
 
@@ -333,6 +359,355 @@
       /***/
     },
 
+    /***/ Lab5: /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      exports.__esModule = true;
+      exports.default = getAssetPathFromRoute; // Translates a logical route into its pages asset path (relative from a common prefix)
+      // "asset path" being its javascript file, data file, prerendered html,...
+
+      function getAssetPathFromRoute(route) {
+        var ext =
+          arguments.length > 1 && arguments[1] !== undefined
+            ? arguments[1]
+            : "";
+        var path =
+          route === "/"
+            ? "/index"
+            : /^\/index(\/|$)/.test(route)
+            ? "/index".concat(route)
+            : "".concat(route);
+        return path + ext;
+      }
+
+      /***/
+    },
+
+    /***/ Nh2W: /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
+      exports.__esModule = true;
+      exports.markAssetError = markAssetError;
+      exports.isAssetError = isAssetError;
+      exports.getClientBuildManifest = getClientBuildManifest;
+      exports.default = void 0;
+
+      var _getAssetPathFromRoute = _interopRequireDefault(
+        __webpack_require__("Lab5")
+      );
+
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      ); // 3.8s was arbitrarily chosen as it's what https://web.dev/interactive
+      // considers as "Good" time-to-interactive. We must assume something went
+      // wrong beyond this point, and then fall-back to a full page transition to
+      // show the user something of value.
+
+      var MS_MAX_IDLE_DELAY = 3800;
+
+      function withFuture(key, map, generator) {
+        var entry = map.get(key);
+
+        if (entry) {
+          if ("future" in entry) {
+            return entry.future;
+          }
+
+          return Promise.resolve(entry);
+        }
+
+        var resolver;
+        var prom = new Promise(resolve => {
+          resolver = resolve;
+        });
+        map.set(
+          key,
+          (entry = {
+            resolve: resolver,
+            future: prom
+          })
+        );
+        return generator // eslint-disable-next-line no-sequences
+          ? generator().then(value => (resolver(value), value))
+          : prom;
+      }
+
+      function hasPrefetch(link) {
+        try {
+          link = document.createElement("link");
+          return link.relList.supports("prefetch");
+        } catch (_unused) {
+          return false;
+        }
+      }
+
+      var canPrefetch = hasPrefetch();
+
+      function prefetchViaDom(href, as, link) {
+        return new Promise((res, rej) => {
+          if (
+            document.querySelector(
+              'link[rel="prefetch"][href^="'.concat(href, '"]')
+            )
+          ) {
+            return res();
+          }
+
+          link = document.createElement("link"); // The order of property assignment here is intentional:
+
+          if (as) link.as = as;
+          link.rel = "prefetch";
+          link.crossOrigin = "anonymous";
+          link.onload = res;
+          link.onerror = rej; // `href` should always be last:
+
+          link.href = href;
+          document.head.appendChild(link);
+        });
+      }
+
+      var ASSET_LOAD_ERROR = Symbol("ASSET_LOAD_ERROR"); // TODO: unexport
+
+      function markAssetError(err) {
+        return Object.defineProperty(err, ASSET_LOAD_ERROR, {});
+      }
+
+      function isAssetError(err) {
+        return err && ASSET_LOAD_ERROR in err;
+      }
+
+      function appendScript(src, script) {
+        return new Promise((resolve, reject) => {
+          script = document.createElement("script"); // The order of property assignment here is intentional.
+          // 1. Setup success/failure hooks in case the browser synchronously
+          //    executes when `src` is set.
+
+          script.onload = resolve;
+
+          script.onerror = () =>
+            reject(
+              markAssetError(new Error("Failed to load script: ".concat(src)))
+            ); // 2. Configure the cross-origin attribute before setting `src` in case the
+          //    browser begins to fetch.
+
+          script.crossOrigin = "anonymous"; // 3. Finally, set the source and inject into the DOM in case the child
+          //    must be appended for fetching to start.
+
+          script.src = src;
+          document.body.appendChild(script);
+        });
+      }
+
+      function idleTimeout(ms, err) {
+        return new Promise((_resolve, reject) =>
+          (0, _requestIdleCallback.default)(() =>
+            setTimeout(() => reject(err), ms)
+          )
+        );
+      } // TODO: stop exporting or cache the failure
+      // It'd be best to stop exporting this. It's an implementation detail. We're
+      // only exporting it for backwards compatibilty with the `page-loader`.
+      // Only cache this response as a last resort if we cannot eliminate all other
+      // code branches that use the Build Manifest Callback and push them through
+      // the Route Loader interface.
+
+      function getClientBuildManifest() {
+        if (self.__BUILD_MANIFEST) {
+          return Promise.resolve(self.__BUILD_MANIFEST);
+        }
+
+        var onBuildManifest = new Promise(resolve => {
+          // Mandatory because this is not concurrent safe:
+          var cb = self.__BUILD_MANIFEST_CB;
+
+          self.__BUILD_MANIFEST_CB = () => {
+            resolve(self.__BUILD_MANIFEST);
+            cb && cb();
+          };
+        });
+        return Promise.race([
+          onBuildManifest,
+          idleTimeout(
+            MS_MAX_IDLE_DELAY,
+            markAssetError(new Error("Failed to load client build manifest"))
+          )
+        ]);
+      }
+
+      function getFilesForRoute(assetPrefix, route) {
+        if (false) {
+        }
+
+        return getClientBuildManifest().then(manifest => {
+          if (!(route in manifest)) {
+            throw markAssetError(
+              new Error("Failed to lookup route: ".concat(route))
+            );
+          }
+
+          var allFiles = manifest[route].map(
+            entry => assetPrefix + "/_next/" + encodeURI(entry)
+          );
+          return {
+            scripts: allFiles.filter(v => v.endsWith(".js")),
+            css: allFiles.filter(v => v.endsWith(".css"))
+          };
+        });
+      }
+
+      function createRouteLoader(assetPrefix) {
+        var entrypoints = new Map();
+        var loadedScripts = new Map();
+        var styleSheets = new Map();
+        var routes = new Map();
+
+        function maybeExecuteScript(src) {
+          var prom = loadedScripts.get(src);
+
+          if (prom) {
+            return prom;
+          } // Skip executing script if it's already in the DOM:
+
+          if (document.querySelector('script[src^="'.concat(src, '"]'))) {
+            return Promise.resolve();
+          }
+
+          loadedScripts.set(src, (prom = appendScript(src)));
+          return prom;
+        }
+
+        function fetchStyleSheet(href) {
+          var prom = styleSheets.get(href);
+
+          if (prom) {
+            return prom;
+          }
+
+          styleSheets.set(
+            href,
+            (prom = fetch(href, {
+              credentials: "include"
+            })
+              .then(res => {
+                if (!res.ok) {
+                  throw new Error("Failed to load stylesheet: ".concat(href));
+                }
+
+                return res.text().then(text => ({
+                  href: href,
+                  content: text
+                }));
+              })
+              .catch(err => {
+                throw markAssetError(err);
+              }))
+          );
+          return prom;
+        }
+
+        return {
+          whenEntrypoint(route) {
+            return withFuture(route, entrypoints);
+          },
+
+          onEntrypoint(route, execute) {
+            Promise.resolve(execute)
+              .then(fn => fn())
+              .then(
+                exports => ({
+                  component: (exports && exports.default) || exports,
+                  exports: exports
+                }),
+                err => ({
+                  error: err
+                })
+              )
+              .then(input => {
+                var old = entrypoints.get(route);
+                entrypoints.set(route, input);
+                if (old && "resolve" in old) old.resolve(input);
+              });
+          },
+
+          loadRoute(route) {
+            return withFuture(route, routes, async () => {
+              try {
+                var { scripts, css } = await getFilesForRoute(
+                  assetPrefix,
+                  route
+                );
+                var [, styles] = await Promise.all([
+                  entrypoints.has(route)
+                    ? []
+                    : Promise.all(scripts.map(maybeExecuteScript)),
+                  Promise.all(css.map(fetchStyleSheet))
+                ]);
+                var entrypoint = await Promise.race([
+                  this.whenEntrypoint(route),
+                  idleTimeout(
+                    MS_MAX_IDLE_DELAY,
+                    markAssetError(
+                      new Error(
+                        "Route did not complete loading: ".concat(route)
+                      )
+                    )
+                  )
+                ]);
+                var res = Object.assign(
+                  {
+                    styles
+                  },
+                  entrypoint
+                );
+                return "error" in entrypoint ? entrypoint : res;
+              } catch (err) {
+                return {
+                  error: err
+                };
+              }
+            });
+          },
+
+          prefetch(route) {
+            // https://github.com/GoogleChromeLabs/quicklink/blob/453a661fa1fa940e2d2e044452398e38c67a98fb/src/index.mjs#L115-L118
+            // License: Apache 2.0
+            var cn;
+
+            if ((cn = navigator.connection)) {
+              // Don't prefetch if using 2G or if Save-Data is enabled.
+              if (cn.saveData || /2g/.test(cn.effectiveType))
+                return Promise.resolve();
+            }
+
+            return getFilesForRoute(assetPrefix, route)
+              .then(output =>
+                Promise.all(
+                  canPrefetch
+                    ? output.scripts.map(script =>
+                        prefetchViaDom(script, "script")
+                      )
+                    : []
+                )
+              )
+              .then(() => {
+                (0, _requestIdleCallback.default)(() => this.loadRoute(route));
+              })
+              .catch(
+                // swallow prefetch errors
+                () => {}
+              );
+          }
+        };
+      }
+
+      var _default = createRouteLoader;
+      exports.default = _default;
+
+      /***/
+    },
+
     /***/ Qetd: /***/ function(module, exports, __webpack_require__) {
       "use strict";
       var assign = Object.assign.bind(Object);
@@ -560,17 +935,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
       exports.isLocalURL = isLocalURL;
       exports.interpolateAs = interpolateAs;
       exports.resolveHref = resolveHref;
-      exports.markLoadingError = markLoadingError;
       exports.default = void 0;
 
       var _normalizeTrailingSlash = __webpack_require__("X24+");
 
+      var _routeLoader = __webpack_require__("Nh2W");
+
       var _denormalizePagePath = __webpack_require__("wkBG");
 
       var _mitt = _interopRequireDefault(__webpack_require__("dZ6Y"));
 
       var _utils = __webpack_require__("g/15");
 
+      var _escapePathDelimiters = _interopRequireDefault(
+        __webpack_require__("fcRV")
+      );
+
       var _isDynamic = __webpack_require__("/jkW");
 
       var _parseRelativeUrl = __webpack_require__("hS4m");
@@ -585,10 +965,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
 
       var _routeRegex = __webpack_require__("YTqd");
 
-      var _escapePathDelimiters = _interopRequireDefault(
-        __webpack_require__("fcRV")
-      );
-
       function _interopRequireDefault(obj) {
         return obj && obj.__esModule
           ? obj
@@ -776,12 +1152,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
         }
       }
 
-      var PAGE_LOAD_ERROR = Symbol("PAGE_LOAD_ERROR");
-
-      function markLoadingError(err) {
-        return Object.defineProperty(err, PAGE_LOAD_ERROR, {});
-      }
-
       function prepareUrlAs(router, url, as) {
         // If url and as provided as an object representation,
         // we'll format them into the string version here.
@@ -833,7 +1203,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
           // on a client-side transition. Otherwise, we'd get into an infinite
           // loop.
           if (!isServerRender) {
-            markLoadingError(err);
+            (0, _routeLoader.markAssetError)(err);
           }
 
           throw err;
@@ -852,7 +1222,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             App,
             wrapApp,
             Component,
-            initialStyleSheets,
             err,
             subscription,
             isFallback,
@@ -949,7 +1318,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
           if (_pathname !== "/_error") {
             this.components[this.route] = {
               Component,
-              styleSheets: initialStyleSheets,
+              initial: true,
               props: initialProps,
               err,
               __N_SSG: initialProps && initialProps.__N_SSG,
@@ -1104,15 +1473,26 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             this.notify(this.components[this.route]);
             Router.events.emit("hashChangeComplete", as);
             return true;
-          } // The build manifest needs to be loaded before auto-static dynamic pages
+          }
+
+          var parsed = (0, _parseRelativeUrl.parseRelativeUrl)(url);
+          var { pathname, query } = parsed; // The build manifest needs to be loaded before auto-static dynamic pages
           // get their query parameters to allow ensuring they can be parsed properly
           // when rewritten to
 
-          var pages = await this.pageLoader.getPageList();
-          var { __rewrites: rewrites } = await this.pageLoader
-            .promisedBuildManifest;
-          var parsed = (0, _parseRelativeUrl.parseRelativeUrl)(url);
-          var { pathname, query } = parsed;
+          var pages, rewrites;
+
+          try {
+            pages = await this.pageLoader.getPageList();
+            ({ __rewrites: rewrites } = await (0,
+            _routeLoader.getClientBuildManifest)());
+          } catch (err) {
+            // If we fail to resolve the page list or client-build manifest, we must
+            // do a server-side transition:
+            window.location.href = as;
+            return false;
+          }
+
           parsed = this._resolveHref(parsed, pages);
 
           if (parsed.pathname !== pathname) {
@@ -1321,7 +1701,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             throw err;
           }
 
-          if (PAGE_LOAD_ERROR in err || loadErrorFail) {
+          if ((0, _routeLoader.isAssetError)(err) || loadErrorFail) {
             Router.events.emit("routeChangeError", err, as); // If we can't load the page it could be one of following reasons
             //  1. Page doesn't exists
             //  2. Page does exist in a different zone
@@ -1417,12 +1797,16 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
               : false;
 
           try {
-            var cachedRouteInfo = this.components[route];
+            var existingRouteInfo = this.components[route];
 
-            if (shallow && cachedRouteInfo && this.route === route) {
-              return cachedRouteInfo;
+            if (shallow && existingRouteInfo && this.route === route) {
+              return existingRouteInfo;
             }
 
+            var cachedRouteInfo =
+              existingRouteInfo && "initial" in existingRouteInfo
+                ? undefined
+                : existingRouteInfo;
             var routeInfo = cachedRouteInfo
               ? cachedRouteInfo
               : await this.fetchComponent(route).then(res => ({
Diff for 677f882d2ed8..43ac7e320.js

Diff too large to display

Diff for main-360c258..7b.module.js
@@ -247,7 +247,7 @@
 
       var _headManager = _interopRequireDefault(__webpack_require__("DqTX"));
 
-      var _pageLoader = _interopRequireWildcard3(__webpack_require__("zmvN"));
+      var _pageLoader = _interopRequireDefault(__webpack_require__("zmvN"));
 
       var _performanceRelayer = _interopRequireDefault(
         __webpack_require__("bGXG")
@@ -262,6 +262,9 @@
       window.__NEXT_DATA__ = data;
       var version = "10.0.2-canary.8";
       exports.version = version;
+
+      var looseToArray = input => [].slice.call(input);
+
       var {
         props: hydrateProps,
         err: hydrateErr,
@@ -301,11 +304,11 @@
           normalizeLocalePath;
       }
 
-      var pageLoader = new _pageLoader.default(buildId, prefix, page);
+      var pageLoader = new _pageLoader.default(buildId, prefix);
 
       var register = _ref => {
         var [r, f] = _ref;
-        return pageLoader.registerPage(r, f);
+        return pageLoader.routeLoader.onEntrypoint(r, f);
       };
 
       if (window.__NEXT_P) {
@@ -324,7 +327,6 @@
       var router;
       exports.router = router;
       var CachedComponent;
-      var cachedStyleSheets;
       var CachedApp, onPerfEntry;
 
       class Container extends _react.default.Component {
@@ -410,7 +412,15 @@
         if (false) {
         }
 
-        var { page: app, mod } = await pageLoader.loadPage("/_app");
+        var appEntrypoint = await pageLoader.routeLoader.whenEntrypoint(
+          "/_app"
+        );
+
+        if ("error" in appEntrypoint) {
+          throw appEntrypoint.error;
+        }
+
+        var { component: app, exports: mod } = appEntrypoint;
         CachedApp = app;
 
         if (mod && mod.reportWebVitals) {
@@ -450,19 +460,23 @@
         var initialErr = hydrateErr;
 
         try {
-          ({
-            page: CachedComponent,
-            styleSheets: cachedStyleSheets
-          } = await pageLoader.loadPage(page));
+          var pageEntrypoint = // The dev server fails to serve script assets when there's a hydration
+            // error, so we need to skip waiting for the entrypoint.
+            false
+              ? undefined
+              : await pageLoader.routeLoader.whenEntrypoint(page);
+
+          if ("error" in pageEntrypoint) {
+            throw pageEntrypoint.error;
+          }
+
+          CachedComponent = pageEntrypoint.component;
 
           if (false) {
             var isValidElementType;
           }
         } catch (error) {
-          if (_pageLoader.INITIAL_CSS_LOAD_ERROR in error) {
-            throw error;
-          } // This catches errors like throwing in the top level of a module
-
+          // This catches errors like throwing in the top level of a module
           initialErr = error;
         }
 
@@ -483,20 +497,15 @@
             pageLoader,
             App: CachedApp,
             Component: CachedComponent,
-            initialStyleSheets: cachedStyleSheets,
             wrapApp,
             err: initialErr,
             isFallback: Boolean(isFallback),
-            subscription: (_ref3, App) => {
-              var { Component, styleSheets, props, err } = _ref3;
-              return render({
-                App,
-                Component,
-                styleSheets,
-                props,
-                err
-              });
-            },
+            subscription: (info, App) =>
+              render(
+                Object.assign({}, info, {
+                  App
+                })
+              ),
             locale,
             locales,
             defaultLocale
@@ -508,8 +517,8 @@
 
         var renderCtx = {
           App: CachedApp,
+          initial: true,
           Component: CachedComponent,
-          styleSheets: cachedStyleSheets,
           props: hydrateProps,
           err: initialErr
         };
@@ -561,8 +570,8 @@
         } // Make sure we log the error to the console, otherwise users can't track down issues.
 
         console.error(err);
-        return pageLoader.loadPage("/_error").then(_ref4 => {
-          var { page: ErrorComponent, styleSheets } = _ref4;
+        return pageLoader.loadPage("/_error").then(_ref3 => {
+          var { page: ErrorComponent, styleSheets } = _ref3;
           // In production we do a normal render with the `ErrorComponent` as component.
           // If we've gotten here upon initial render, we can use the props from the server.
           // Otherwise, we need to call `getInitialProps` on `App` before mounting.
@@ -594,9 +603,8 @@
             )
           );
         });
-      } // If hydrate does not exist, eg in preact.
+      }
 
-      var isInitialRender = typeof _reactDom.default.hydrate === "function";
       var reactRoot = null;
 
       function renderReactElement(reactEl, domEl) {
@@ -608,10 +616,8 @@
             performance.mark("beforeRender");
           } // The check for `.hydrate` is there to support React alternatives like preact
 
-          if (isInitialRender) {
+          if (typeof _reactDom.default.hydrate === "function") {
             _reactDom.default.hydrate(reactEl, domEl, markHydrateComplete);
-
-            isInitialRender = false;
           } else {
             _reactDom.default.render(reactEl, domEl, markRenderComplete);
           }
@@ -684,8 +690,8 @@
         ].forEach(mark => performance.clearMarks(mark));
       }
 
-      function AppContainer(_ref5) {
-        var { children } = _ref5;
+      function AppContainer(_ref4) {
+        var { children } = _ref4;
         return /*#__PURE__*/ _react.default.createElement(
           Container,
           {
@@ -724,8 +730,9 @@
         );
       };
 
-      function doRender(_ref6) {
-        var { App, Component, props, err, styleSheets } = _ref6;
+      function doRender(input) {
+        var { App, Component, props, err } = input;
+        var styleSheets = "initial" in input ? undefined : input.styleSheets;
         Component = Component || lastAppProps.Component;
         props = props || lastAppProps.props;
         var appProps = (0, _extends2.default)({}, props, {
@@ -759,23 +766,21 @@
 
         function onStart() {
           if (
-            // We can skip this during hydration. Running it wont cause any harm, but
-            // we may as well save the CPU cycles.
-            isInitialRender || // We use `style-loader` in development, so we don't need to do anything
+            !styleSheets || // We use `style-loader` in development, so we don't need to do anything
             // unless we're in production:
             false
           ) {
             return false;
           }
 
-          var currentStyleTags = (0, _pageLoader.looseToArray)(
+          var currentStyleTags = looseToArray(
             document.querySelectorAll("style[data-n-href]")
           );
           var currentHrefs = new Set(
             currentStyleTags.map(tag => tag.getAttribute("data-n-href"))
           );
-          styleSheets.forEach(_ref7 => {
-            var { href, text } = _ref7;
+          styleSheets.forEach(_ref5 => {
+            var { href, text } = _ref5;
 
             if (!currentHrefs.has(href)) {
               var styleTag = document.createElement("style");
@@ -794,11 +799,11 @@
             // unless we're in production:
             true && // We can skip this during hydration. Running it wont cause any harm, but
             // we may as well save the CPU cycles:
-            !isInitialRender && // Ensure this render was not canceled
+            styleSheets && // Ensure this render was not canceled
             !canceled
           ) {
             var desiredHrefs = new Set(styleSheets.map(s => s.href));
-            var currentStyleTags = (0, _pageLoader.looseToArray)(
+            var currentStyleTags = looseToArray(
               document.querySelectorAll("style[data-n-href]")
             );
             var currentHrefs = currentStyleTags.map(tag =>
@@ -819,8 +824,8 @@
               // This should be an invariant:
               referenceNode
             ) {
-              styleSheets.forEach(_ref8 => {
-                var { href } = _ref8;
+              styleSheets.forEach(_ref6 => {
+                var { href } = _ref6;
                 var targetTag = document.querySelector(
                   'style[data-n-href="'.concat(href, '"]')
                 );
@@ -838,11 +843,11 @@
               });
             } // Finally, clean up server rendered stylesheets:
 
-            (0, _pageLoader.looseToArray)(
-              document.querySelectorAll("link[data-n-p]")
-            ).forEach(el => {
-              el.parentNode.removeChild(el);
-            }); // Force browser to recompute layout, which should prevent a flash of
+            looseToArray(document.querySelectorAll("link[data-n-p]")).forEach(
+              el => {
+                el.parentNode.removeChild(el);
+              }
+            ); // Force browser to recompute layout, which should prevent a flash of
             // unstyled content:
 
             getComputedStyle(document.body, "height");
@@ -869,8 +874,8 @@
         return renderPromise;
       }
 
-      function Root(_ref9) {
-        var { callback, children } = _ref9;
+      function Root(_ref7) {
+        var { callback, children } = _ref7;
 
         // We use `useLayoutEffect` to guarantee the callback is executed
         // as soon as React flushes the update.
@@ -898,30 +903,6 @@
       /***/
     },
 
-    /***/ Lab5: /***/ function(module, exports, __webpack_require__) {
-      "use strict";
-
-      exports.__esModule = true;
-      exports.default = getAssetPathFromRoute; // Translates a logical route into its pages asset path (relative from a common prefix)
-      // "asset path" being its javascript file, data file, prerendered html,...
-
-      function getAssetPathFromRoute(route) {
-        var ext =
-          arguments.length > 1 && arguments[1] !== undefined
-            ? arguments[1]
-            : "";
-        var path =
-          route === "/"
-            ? "/index"
-            : /^\/index(\/|$)/.test(route)
-            ? "/index".concat(route)
-            : "".concat(route);
-        return path + ext;
-      }
-
-      /***/
-    },
-
     /***/ bGXG: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
@@ -1277,12 +1258,12 @@
     /***/ zmvN: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
+      var _interopRequireWildcard = __webpack_require__("284h");
+
       var _interopRequireDefault = __webpack_require__("TqRt");
 
       exports.__esModule = true;
-      exports.default = exports.INITIAL_CSS_LOAD_ERROR = exports.looseToArray = void 0;
-
-      var _mitt = _interopRequireDefault(__webpack_require__("dZ6Y"));
+      exports.default = void 0;
 
       var _router = __webpack_require__("elyg");
 
@@ -1294,32 +1275,7 @@
 
       var _parseRelativeUrl = __webpack_require__("hS4m");
 
-      var looseToArray = input => [].slice.call(input);
-
-      exports.looseToArray = looseToArray;
-
-      function hasRel(rel, link) {
-        try {
-          link = document.createElement("link");
-          return link.relList.supports(rel);
-        } catch (_unused) {}
-      }
-
-      function pageLoadError(route) {
-        return (0, _router.markLoadingError)(
-          new Error("Error loading ".concat(route))
-        );
-      }
-
-      var INITIAL_CSS_LOAD_ERROR = Symbol("INITIAL_CSS_LOAD_ERROR");
-      exports.INITIAL_CSS_LOAD_ERROR = INITIAL_CSS_LOAD_ERROR;
-      var relPrefetch =
-        hasRel("preload") && !hasRel("prefetch") // https://caniuse.com/#feat=link-rel-preload
-          ? // macOS and iOS (Safari does not support prefetch)
-            "preload" // https://caniuse.com/#feat=link-rel-prefetch
-          : // IE 11, Edge 12+, nearly all evergreen
-            "prefetch";
-      var hasNoModule = "noModule" in document.createElement("script");
+      var _routeLoader = _interopRequireWildcard(__webpack_require__("Nh2W"));
 
       function normalizeRoute(route) {
         if (route[0] !== "/") {
@@ -1332,75 +1288,16 @@
         return route.replace(/\/$/, "");
       }
 
-      function appendLink(href, rel, as, link) {
-        return new Promise((res, rej) => {
-          link = document.createElement("link"); // The order of property assignment here is intentional:
-
-          if (as) link.as = as;
-          link.rel = rel;
-          link.crossOrigin = "anonymous";
-          link.onload = res;
-          link.onerror = rej; // `href` should always be last:
-
-          link.href = href;
-          document.head.appendChild(link);
-        });
-      }
-
-      function loadScript(url) {
-        return new Promise((res, rej) => {
-          var script = document.createElement("script");
-
-          if (true && hasNoModule) {
-            script.type = "module";
-          }
-
-          script.crossOrigin = "anonymous";
-          script.src = url;
-          script.onload = res;
-
-          script.onerror = () => rej(pageLoadError(url));
-
-          document.body.appendChild(script);
-        });
-      }
-
       class PageLoader {
-        constructor(buildId, assetPrefix, initialPage) {
-          this.initialPage = void 0;
+        constructor(buildId, assetPrefix) {
           this.buildId = void 0;
           this.assetPrefix = void 0;
-          this.pageCache = void 0;
-          this.cssc = void 0;
-          this.pageRegisterEvents = void 0;
-          this.loadingRoutes = void 0;
-          this.promisedBuildManifest = void 0;
           this.promisedSsgManifest = void 0;
           this.promisedDevPagesManifest = void 0;
-          this.initialPage = initialPage;
+          this.routeLoader = void 0;
+          this.routeLoader = (0, _routeLoader.default)(assetPrefix);
           this.buildId = buildId;
           this.assetPrefix = assetPrefix;
-          this.pageCache = {};
-          this.cssc = {};
-          this.pageRegisterEvents = (0, _mitt.default)();
-          this.loadingRoutes = {
-            // By default these 2 pages are being loaded in the initial html
-            "/_app": true
-          }; // TODO: get rid of this limitation for rendering the error page
-
-          if (initialPage !== "/_error") {
-            this.loadingRoutes[initialPage] = true;
-          }
-
-          this.promisedBuildManifest = new Promise(resolve => {
-            if (window.__BUILD_MANIFEST) {
-              resolve(window.__BUILD_MANIFEST);
-            } else {
-              window.__BUILD_MANIFEST_CB = () => {
-                resolve(window.__BUILD_MANIFEST);
-              };
-            }
-          });
           /** @type {Promise<Set<string>>} */
 
           this.promisedSsgManifest = new Promise(resolve => {
@@ -1416,36 +1313,12 @@
 
         getPageList() {
           if (true) {
-            return this.promisedBuildManifest.then(
-              buildManifest => buildManifest.sortedPages
+            return (0, _routeLoader.getClientBuildManifest)().then(
+              manifest => manifest.sortedPages
             );
           } else {
           }
         }
-
-        fetchStyleSheet(href) {
-          if (!this.cssc[href]) {
-            this.cssc[href] = fetch(href).then(res => {
-              if (!res.ok) throw pageLoadError(href);
-              return res.text();
-            });
-          }
-
-          return this.cssc[href].then(text => ({
-            href,
-            text
-          }));
-        } // Returns a promise for the dependencies for a particular route
-
-        getDependencies(route) {
-          return this.promisedBuildManifest.then(m => {
-            return m[route]
-              ? m[route].map(url =>
-                  "".concat(this.assetPrefix, "/_next/").concat(encodeURI(url))
-                )
-              : Promise.reject(pageLoadError(route));
-          });
-        }
         /**
          * @param {string} href the route href (file-system path)
          * @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
@@ -1491,190 +1364,24 @@
         }
 
         loadPage(route) {
-          route = normalizeRoute(route);
-          return new Promise((resolve, reject) => {
-            // If there's a cached version of the page, let's use it.
-            var cachedPage = this.pageCache[route];
-
-            if (cachedPage) {
-              if ("error" in cachedPage) {
-                reject(cachedPage.error);
-              } else {
-                resolve(cachedPage);
-              }
-
-              return;
-            }
-
-            var fire = pageToCache => {
-              this.pageRegisterEvents.off(route, fire);
-              delete this.loadingRoutes[route];
-
-              if ("error" in pageToCache) {
-                reject(pageToCache.error);
-              } else {
-                resolve(pageToCache);
-              }
-            }; // Register a listener to get the page
-
-            this.pageRegisterEvents.on(route, fire);
-
-            if (!this.loadingRoutes[route]) {
-              this.loadingRoutes[route] = true;
-
-              if (true) {
-                this.getDependencies(route)
-                  .then(deps => {
-                    var pending = [];
-                    deps.forEach(d => {
-                      if (
-                        d.endsWith(".js") &&
-                        !document.querySelector('script[src^="'.concat(d, '"]'))
-                      ) {
-                        pending.push(loadScript(d));
-                      }
-
-                      if (d.endsWith(".css")) {
-                        pending.push(this.fetchStyleSheet(d));
-                      }
-                    });
-                    return Promise.all(pending);
-                  })
-                  .catch(err => {
-                    // Mark the page as failed to load if any of its required scripts
-                    // fail to load:
-                    this.pageCache[route] = {
-                      error: err
-                    };
-                    fire({
-                      error: err
-                    });
-                  });
-              } else {
-                var url, scriptRoute;
-              }
-            }
-          });
-        } // This method if called by the route code.
-
-        registerPage(route, regFn) {
-          var _this = this;
-
-          var register = async function register(styleSheets) {
-            try {
-              var mod = await regFn();
-              var pageData = {
-                page: mod.default || mod,
-                mod,
-                styleSheets
+          return this.routeLoader.loadRoute(route).then(res => {
+            if ("component" in res) {
+              return {
+                page: res.component,
+                mod: res.exports,
+                styleSheets: res.styles.map(o => ({
+                  href: o.href,
+                  text: o.content
+                }))
               };
-              _this.pageCache[route] = pageData;
-
-              _this.pageRegisterEvents.emit(route, pageData);
-            } catch (error) {
-              _this.pageCache[route] = {
-                error
-              };
-
-              _this.pageRegisterEvents.emit(route, {
-                error
-              });
             }
-          };
-
-          if (false) {
-            var check;
-          }
 
-          var isInitialLoad = route === this.initialPage;
-          var promisedDeps = // Shared styles will already be on the page:
-            route === "/_app" || false // We use `style-loader` in development:
-              ? Promise.resolve([]) // Tests that this does not block hydration:
-              : // test/integration/css-fixtures/hydrate-without-deps/
-                (isInitialLoad
-                  ? Promise.resolve(
-                      looseToArray(
-                        document.querySelectorAll("link[data-n-p]")
-                      ).map(e => e.getAttribute("href"))
-                    )
-                  : this.getDependencies(route).then(deps =>
-                      deps.filter(d => d.endsWith(".css"))
-                    )
-                ).then((
-                  cssFiles // These files should've already been fetched by now, so this
-                ) =>
-                  // should resolve instantly.
-                  Promise.all(cssFiles.map(d => this.fetchStyleSheet(d))).catch(
-                    err => {
-                      if (isInitialLoad) {
-                        Object.defineProperty(err, INITIAL_CSS_LOAD_ERROR, {});
-                      }
-
-                      throw err;
-                    }
-                  )
-                );
-          promisedDeps.then(
-            deps => register(deps),
-            error => {
-              this.pageCache[route] = {
-                error
-              };
-              this.pageRegisterEvents.emit(route, {
-                error
-              });
-            }
-          );
+            throw res.error;
+          });
         }
-        /**
-         * @param {string} route
-         * @param {boolean} [isDependency]
-         */
-
-        prefetch(route, isDependency) {
-          // https://github.com/GoogleChromeLabs/quicklink/blob/453a661fa1fa940e2d2e044452398e38c67a98fb/src/index.mjs#L115-L118
-          // License: Apache 2.0
-          var cn;
-
-          if ((cn = navigator.connection)) {
-            // Don't prefetch if using 2G or if Save-Data is enabled.
-            if (cn.saveData || /2g/.test(cn.effectiveType))
-              return Promise.resolve();
-          }
-          /** @type {string} */
-
-          var url;
 
-          if (isDependency) {
-            url = route;
-          } else {
-            if (false) {
-              var scriptRoute, ext;
-            }
-          }
-
-          return Promise.all([
-            url
-              ? url.endsWith(".css")
-                ? this.fetchStyleSheet(url)
-                : !document.querySelector(
-                    'link[rel="'
-                      .concat(relPrefetch, '"][href^="')
-                      .concat(url, '"]')
-                  ) && appendLink(url, relPrefetch, "script")
-              : 0,
-            true &&
-              !isDependency &&
-              this.getDependencies(route).then(urls =>
-                Promise.all(
-                  urls.map(dependencyUrl => this.prefetch(dependencyUrl, true))
-                )
-              )
-          ]).then(
-            // do not return any data
-            () => {}, // swallow prefetch errors
-            () => {}
-          );
+        prefetch(route) {
+          return this.routeLoader.prefetch(route);
         }
       }
Diff for main-4350573..c88c1a7e0.js

Diff too large to display

Diff for index.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      href="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -82,13 +82,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-4350573f106c88c1a7e0.js"
+      src="/_next/static/chunks/main-d0d129f827206e9b7486.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      src="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -118,13 +118,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
Diff for link.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      href="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -37,7 +37,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/pages/link-ec1fbf762c0209fe4b72.module.js"
+      href="/_next/static/chunks/pages/link-cd84dab1f87965855b1c.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -87,13 +87,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-4350573f106c88c1a7e0.js"
+      src="/_next/static/chunks/main-d0d129f827206e9b7486.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      src="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -123,13 +123,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -147,13 +147,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/pages/link-3125d338b922d5ab1776.js"
+      src="/_next/static/chunks/pages/link-4218f7a274c65b7f23dd.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/pages/link-ec1fbf762c0209fe4b72.module.js"
+      src="/_next/static/chunks/pages/link-cd84dab1f87965855b1c.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
Diff for withRouter.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      href="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -82,13 +82,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-4350573f106c88c1a7e0.js"
+      src="/_next/static/chunks/main-d0d129f827206e9b7486.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-360c25811a91b7011b7b.module.js"
+      src="/_next/static/chunks/main-6e2867705f1dff449c07.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -118,13 +118,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"

Serverless Mode (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
buildDuration 15.3s 15.7s ⚠️ +323ms
nodeModulesSize 84.8 MB 84.8 MB ⚠️ +5.05 kB
Client Bundles (main, webpack, commons) Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..01e7.js gzip 11.3 kB N/A N/A
framework.HASH.js gzip 39 kB 39 kB
main-3e470be..d458.js gzip 7.48 kB N/A N/A
webpack-e067..f178.js gzip 751 B 751 B
677f882d2ed8..aa5f.js gzip N/A 12.6 kB N/A
main-8653720..d844.js gzip N/A 6.48 kB N/A
Overall change 58.5 kB 58.8 kB ⚠️ +347 B
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..dule.js gzip 7.05 kB N/A N/A
framework.HA..dule.js gzip 39 kB 39 kB
main-0c7333c..dule.js gzip 6.47 kB N/A N/A
webpack-07c5..dule.js gzip 751 B 751 B
677f882d2ed8..dule.js gzip N/A 8.21 kB N/A
main-a506355..dule.js gzip N/A 5.58 kB N/A
Overall change 53.2 kB 53.5 kB ⚠️ +275 B
Legacy Client Bundles (polyfills)
vercel/next.js canary Timer/next.js refactor/route-loader Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-0abe79e..7616.js gzip 1.55 kB N/A N/A
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
link-d9a15a0..130a.js gzip N/A 1.57 kB N/A
Overall change 7.96 kB 7.98 kB ⚠️ +20 B
Client Pages Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-d1cfb87..dule.js gzip 1.49 kB N/A N/A
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
link-973cce3..dule.js gzip N/A 1.51 kB N/A
Overall change 5.59 kB 5.6 kB ⚠️ +17 B
Client Build Manifests
vercel/next.js canary Timer/next.js refactor/route-loader Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_error.js 916 kB 916 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 916 kB 916 kB
link.js 965 kB 974 kB ⚠️ +9.03 kB
routerDirect.js 958 kB 967 kB ⚠️ +8.85 kB
withRouter.js 958 kB 967 kB ⚠️ +8.85 kB
Overall change 4.72 MB 4.75 MB ⚠️ +26.7 kB
Commit: 49f08e6

Copy link
Member

@ijjk ijjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and is working great in local testing! 🎉 🎉 🎉

@ijjk
Copy link
Member

ijjk commented Nov 11, 2020

Stats from current PR

Default Server Mode (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
buildDuration 12.9s 14.2s ⚠️ +1.3s
nodeModulesSize 84.8 MB 84.8 MB ⚠️ +5.05 kB
Page Load Tests Overall decrease ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
/ failed reqs 0 0
/ total time (seconds) 2.323 2.509 ⚠️ +0.19
/ avg req/sec 1076.04 996.35 ⚠️ -79.69
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.272 1.342 ⚠️ +0.07
/error-in-render avg req/sec 1965.97 1863.19 ⚠️ -102.78
Client Bundles (main, webpack, commons) Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..01e7.js gzip 11.3 kB 12.6 kB ⚠️ +1.34 kB
framework.HASH.js gzip 39 kB 39 kB
main-73b1388..ecd4.js gzip 7.48 kB N/A N/A
webpack-e067..f178.js gzip 751 B 751 B
main-bc384f4..b33c.js gzip N/A 6.48 kB N/A
Overall change 58.5 kB 58.8 kB ⚠️ +347 B
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..dule.js gzip 7.05 kB 8.21 kB ⚠️ +1.16 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-d0704cd..dule.js gzip 6.47 kB 5.58 kB -882 B
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.2 kB 53.5 kB ⚠️ +275 B
Legacy Client Bundles (polyfills)
vercel/next.js canary Timer/next.js refactor/route-loader Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-0abe79e..7616.js gzip 1.55 kB 1.57 kB ⚠️ +20 B
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.96 kB 7.98 kB ⚠️ +20 B
Client Pages Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-d1cfb87..dule.js gzip 1.49 kB 1.51 kB ⚠️ +17 B
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.59 kB 5.6 kB ⚠️ +17 B
Client Build Manifests
vercel/next.js canary Timer/next.js refactor/route-loader Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
index.html gzip 969 B 970 B ⚠️ +1 B
link.html gzip 977 B 978 B ⚠️ +1 B
withRouter.html gzip 961 B 963 B ⚠️ +2 B
Overall change 2.91 kB 2.91 kB ⚠️ +4 B

Diffs

Diff for _buildManifest.js
@@ -7,7 +7,7 @@ self.__BUILD_MANIFEST = {
   "/hooks": [
     "static\u002Fchunks\u002Fpages\u002Fhooks-880757f1d73d6b241fc3.js"
   ],
-  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-3125d338b922d5ab1776.js"],
+  "/link": ["static\u002Fchunks\u002Fpages\u002Flink-4218f7a274c65b7f23dd.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-2e9bfd441bd88cd3382e.js"
   ],
Diff for _buildManifest.module.js
@@ -10,7 +10,7 @@ self.__BUILD_MANIFEST = {
     "static\u002Fchunks\u002Fpages\u002Fhooks-56fa58a6f0993d7d36d7.module.js"
   ],
   "/link": [
-    "static\u002Fchunks\u002Fpages\u002Flink-ec1fbf762c0209fe4b72.module.js"
+    "static\u002Fchunks\u002Fpages\u002Flink-cd84dab1f87965855b1c.module.js"
   ],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-368af3dfef3c9cd99dc3.module.js"
Diff for link-3125d33..2d5ab1776.js
@@ -302,11 +302,17 @@ _N_E = (window["webpackJsonp_N_E"] = window["webpackJsonp_N_E"] || []).push([
 
       var _slicedToArray = __webpack_require__("J4zp");
 
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
       exports.__esModule = true;
       exports.useIntersection = useIntersection;
 
       var _react = __webpack_require__("q1tI");
 
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      );
+
       var hasIntersectionObserver = typeof IntersectionObserver !== "undefined";
 
       function useIntersection(_ref) {
@@ -346,7 +352,10 @@ _N_E = (window["webpackJsonp_N_E"] = window["webpackJsonp_N_E"] || []).push([
         (0, _react.useEffect)(
           function() {
             if (!hasIntersectionObserver) {
-              if (!visible) setVisible(true);
+              if (!visible)
+                (0, _requestIdleCallback["default"])(function() {
+                  return setVisible(true);
+                });
             }
           },
           [visible]
Diff for link-ec1fbf7..72.module.js
@@ -277,11 +277,17 @@
     /***/ vNVm: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
       exports.__esModule = true;
       exports.useIntersection = useIntersection;
 
       var _react = __webpack_require__("q1tI");
 
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      );
+
       var hasIntersectionObserver = typeof IntersectionObserver !== "undefined";
 
       function useIntersection(_ref) {
@@ -312,7 +318,8 @@
         );
         (0, _react.useEffect)(() => {
           if (!hasIntersectionObserver) {
-            if (!visible) setVisible(true);
+            if (!visible)
+              (0, _requestIdleCallback.default)(() => setVisible(true));
           }
         }, [visible]);
         return [setRef, visible];
Diff for 677f882d2ed8..cf.module.js
@@ -55,6 +55,32 @@
       /***/
     },
 
+    /***/ "0G5g": /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      exports.__esModule = true;
+      exports.default = void 0;
+
+      var requestIdleCallback =
+        (typeof self !== "undefined" && self.requestIdleCallback) ||
+        function(cb) {
+          var start = Date.now();
+          return setTimeout(function() {
+            cb({
+              didTimeout: false,
+              timeRemaining: function timeRemaining() {
+                return Math.max(0, 50 - (Date.now() - start));
+              }
+            });
+          }, 1);
+        };
+
+      var _default = requestIdleCallback;
+      exports.default = _default;
+
+      /***/
+    },
+
     /***/ "284h": /***/ function(module, exports, __webpack_require__) {
       var _typeof = __webpack_require__("cDf5");
 
@@ -333,6 +359,355 @@
       /***/
     },
 
+    /***/ Lab5: /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      exports.__esModule = true;
+      exports.default = getAssetPathFromRoute; // Translates a logical route into its pages asset path (relative from a common prefix)
+      // "asset path" being its javascript file, data file, prerendered html,...
+
+      function getAssetPathFromRoute(route) {
+        var ext =
+          arguments.length > 1 && arguments[1] !== undefined
+            ? arguments[1]
+            : "";
+        var path =
+          route === "/"
+            ? "/index"
+            : /^\/index(\/|$)/.test(route)
+            ? "/index".concat(route)
+            : "".concat(route);
+        return path + ext;
+      }
+
+      /***/
+    },
+
+    /***/ Nh2W: /***/ function(module, exports, __webpack_require__) {
+      "use strict";
+
+      var _interopRequireDefault = __webpack_require__("TqRt");
+
+      exports.__esModule = true;
+      exports.markAssetError = markAssetError;
+      exports.isAssetError = isAssetError;
+      exports.getClientBuildManifest = getClientBuildManifest;
+      exports.default = void 0;
+
+      var _getAssetPathFromRoute = _interopRequireDefault(
+        __webpack_require__("Lab5")
+      );
+
+      var _requestIdleCallback = _interopRequireDefault(
+        __webpack_require__("0G5g")
+      ); // 3.8s was arbitrarily chosen as it's what https://web.dev/interactive
+      // considers as "Good" time-to-interactive. We must assume something went
+      // wrong beyond this point, and then fall-back to a full page transition to
+      // show the user something of value.
+
+      var MS_MAX_IDLE_DELAY = 3800;
+
+      function withFuture(key, map, generator) {
+        var entry = map.get(key);
+
+        if (entry) {
+          if ("future" in entry) {
+            return entry.future;
+          }
+
+          return Promise.resolve(entry);
+        }
+
+        var resolver;
+        var prom = new Promise(resolve => {
+          resolver = resolve;
+        });
+        map.set(
+          key,
+          (entry = {
+            resolve: resolver,
+            future: prom
+          })
+        );
+        return generator // eslint-disable-next-line no-sequences
+          ? generator().then(value => (resolver(value), value))
+          : prom;
+      }
+
+      function hasPrefetch(link) {
+        try {
+          link = document.createElement("link");
+          return link.relList.supports("prefetch");
+        } catch (_unused) {
+          return false;
+        }
+      }
+
+      var canPrefetch = hasPrefetch();
+
+      function prefetchViaDom(href, as, link) {
+        return new Promise((res, rej) => {
+          if (
+            document.querySelector(
+              'link[rel="prefetch"][href^="'.concat(href, '"]')
+            )
+          ) {
+            return res();
+          }
+
+          link = document.createElement("link"); // The order of property assignment here is intentional:
+
+          if (as) link.as = as;
+          link.rel = "prefetch";
+          link.crossOrigin = "anonymous";
+          link.onload = res;
+          link.onerror = rej; // `href` should always be last:
+
+          link.href = href;
+          document.head.appendChild(link);
+        });
+      }
+
+      var ASSET_LOAD_ERROR = Symbol("ASSET_LOAD_ERROR"); // TODO: unexport
+
+      function markAssetError(err) {
+        return Object.defineProperty(err, ASSET_LOAD_ERROR, {});
+      }
+
+      function isAssetError(err) {
+        return err && ASSET_LOAD_ERROR in err;
+      }
+
+      function appendScript(src, script) {
+        return new Promise((resolve, reject) => {
+          script = document.createElement("script"); // The order of property assignment here is intentional.
+          // 1. Setup success/failure hooks in case the browser synchronously
+          //    executes when `src` is set.
+
+          script.onload = resolve;
+
+          script.onerror = () =>
+            reject(
+              markAssetError(new Error("Failed to load script: ".concat(src)))
+            ); // 2. Configure the cross-origin attribute before setting `src` in case the
+          //    browser begins to fetch.
+
+          script.crossOrigin = "anonymous"; // 3. Finally, set the source and inject into the DOM in case the child
+          //    must be appended for fetching to start.
+
+          script.src = src;
+          document.body.appendChild(script);
+        });
+      }
+
+      function idleTimeout(ms, err) {
+        return new Promise((_resolve, reject) =>
+          (0, _requestIdleCallback.default)(() =>
+            setTimeout(() => reject(err), ms)
+          )
+        );
+      } // TODO: stop exporting or cache the failure
+      // It'd be best to stop exporting this. It's an implementation detail. We're
+      // only exporting it for backwards compatibilty with the `page-loader`.
+      // Only cache this response as a last resort if we cannot eliminate all other
+      // code branches that use the Build Manifest Callback and push them through
+      // the Route Loader interface.
+
+      function getClientBuildManifest() {
+        if (self.__BUILD_MANIFEST) {
+          return Promise.resolve(self.__BUILD_MANIFEST);
+        }
+
+        var onBuildManifest = new Promise(resolve => {
+          // Mandatory because this is not concurrent safe:
+          var cb = self.__BUILD_MANIFEST_CB;
+
+          self.__BUILD_MANIFEST_CB = () => {
+            resolve(self.__BUILD_MANIFEST);
+            cb && cb();
+          };
+        });
+        return Promise.race([
+          onBuildManifest,
+          idleTimeout(
+            MS_MAX_IDLE_DELAY,
+            markAssetError(new Error("Failed to load client build manifest"))
+          )
+        ]);
+      }
+
+      function getFilesForRoute(assetPrefix, route) {
+        if (false) {
+        }
+
+        return getClientBuildManifest().then(manifest => {
+          if (!(route in manifest)) {
+            throw markAssetError(
+              new Error("Failed to lookup route: ".concat(route))
+            );
+          }
+
+          var allFiles = manifest[route].map(
+            entry => assetPrefix + "/_next/" + encodeURI(entry)
+          );
+          return {
+            scripts: allFiles.filter(v => v.endsWith(".js")),
+            css: allFiles.filter(v => v.endsWith(".css"))
+          };
+        });
+      }
+
+      function createRouteLoader(assetPrefix) {
+        var entrypoints = new Map();
+        var loadedScripts = new Map();
+        var styleSheets = new Map();
+        var routes = new Map();
+
+        function maybeExecuteScript(src) {
+          var prom = loadedScripts.get(src);
+
+          if (prom) {
+            return prom;
+          } // Skip executing script if it's already in the DOM:
+
+          if (document.querySelector('script[src^="'.concat(src, '"]'))) {
+            return Promise.resolve();
+          }
+
+          loadedScripts.set(src, (prom = appendScript(src)));
+          return prom;
+        }
+
+        function fetchStyleSheet(href) {
+          var prom = styleSheets.get(href);
+
+          if (prom) {
+            return prom;
+          }
+
+          styleSheets.set(
+            href,
+            (prom = fetch(href, {
+              credentials: "include"
+            })
+              .then(res => {
+                if (!res.ok) {
+                  throw new Error("Failed to load stylesheet: ".concat(href));
+                }
+
+                return res.text().then(text => ({
+                  href: href,
+                  content: text
+                }));
+              })
+              .catch(err => {
+                throw markAssetError(err);
+              }))
+          );
+          return prom;
+        }
+
+        return {
+          whenEntrypoint(route) {
+            return withFuture(route, entrypoints);
+          },
+
+          onEntrypoint(route, execute) {
+            Promise.resolve(execute)
+              .then(fn => fn())
+              .then(
+                exports => ({
+                  component: (exports && exports.default) || exports,
+                  exports: exports
+                }),
+                err => ({
+                  error: err
+                })
+              )
+              .then(input => {
+                var old = entrypoints.get(route);
+                entrypoints.set(route, input);
+                if (old && "resolve" in old) old.resolve(input);
+              });
+          },
+
+          loadRoute(route) {
+            return withFuture(route, routes, async () => {
+              try {
+                var { scripts, css } = await getFilesForRoute(
+                  assetPrefix,
+                  route
+                );
+                var [, styles] = await Promise.all([
+                  entrypoints.has(route)
+                    ? []
+                    : Promise.all(scripts.map(maybeExecuteScript)),
+                  Promise.all(css.map(fetchStyleSheet))
+                ]);
+                var entrypoint = await Promise.race([
+                  this.whenEntrypoint(route),
+                  idleTimeout(
+                    MS_MAX_IDLE_DELAY,
+                    markAssetError(
+                      new Error(
+                        "Route did not complete loading: ".concat(route)
+                      )
+                    )
+                  )
+                ]);
+                var res = Object.assign(
+                  {
+                    styles
+                  },
+                  entrypoint
+                );
+                return "error" in entrypoint ? entrypoint : res;
+              } catch (err) {
+                return {
+                  error: err
+                };
+              }
+            });
+          },
+
+          prefetch(route) {
+            // https://github.com/GoogleChromeLabs/quicklink/blob/453a661fa1fa940e2d2e044452398e38c67a98fb/src/index.mjs#L115-L118
+            // License: Apache 2.0
+            var cn;
+
+            if ((cn = navigator.connection)) {
+              // Don't prefetch if using 2G or if Save-Data is enabled.
+              if (cn.saveData || /2g/.test(cn.effectiveType))
+                return Promise.resolve();
+            }
+
+            return getFilesForRoute(assetPrefix, route)
+              .then(output =>
+                Promise.all(
+                  canPrefetch
+                    ? output.scripts.map(script =>
+                        prefetchViaDom(script, "script")
+                      )
+                    : []
+                )
+              )
+              .then(() => {
+                (0, _requestIdleCallback.default)(() => this.loadRoute(route));
+              })
+              .catch(
+                // swallow prefetch errors
+                () => {}
+              );
+          }
+        };
+      }
+
+      var _default = createRouteLoader;
+      exports.default = _default;
+
+      /***/
+    },
+
     /***/ Qetd: /***/ function(module, exports, __webpack_require__) {
       "use strict";
       var assign = Object.assign.bind(Object);
@@ -560,17 +935,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
       exports.isLocalURL = isLocalURL;
       exports.interpolateAs = interpolateAs;
       exports.resolveHref = resolveHref;
-      exports.markLoadingError = markLoadingError;
       exports.default = void 0;
 
       var _normalizeTrailingSlash = __webpack_require__("X24+");
 
+      var _routeLoader = __webpack_require__("Nh2W");
+
       var _denormalizePagePath = __webpack_require__("wkBG");
 
       var _mitt = _interopRequireDefault(__webpack_require__("dZ6Y"));
 
       var _utils = __webpack_require__("g/15");
 
+      var _escapePathDelimiters = _interopRequireDefault(
+        __webpack_require__("fcRV")
+      );
+
       var _isDynamic = __webpack_require__("/jkW");
 
       var _parseRelativeUrl = __webpack_require__("hS4m");
@@ -585,10 +965,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
 
       var _routeRegex = __webpack_require__("YTqd");
 
-      var _escapePathDelimiters = _interopRequireDefault(
-        __webpack_require__("fcRV")
-      );
-
       function _interopRequireDefault(obj) {
         return obj && obj.__esModule
           ? obj
@@ -776,12 +1152,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
         }
       }
 
-      var PAGE_LOAD_ERROR = Symbol("PAGE_LOAD_ERROR");
-
-      function markLoadingError(err) {
-        return Object.defineProperty(err, PAGE_LOAD_ERROR, {});
-      }
-
       function prepareUrlAs(router, url, as) {
         // If url and as provided as an object representation,
         // we'll format them into the string version here.
@@ -833,7 +1203,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
           // on a client-side transition. Otherwise, we'd get into an infinite
           // loop.
           if (!isServerRender) {
-            markLoadingError(err);
+            (0, _routeLoader.markAssetError)(err);
           }
 
           throw err;
@@ -852,7 +1222,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             App,
             wrapApp,
             Component,
-            initialStyleSheets,
             err,
             subscription,
             isFallback,
@@ -949,7 +1318,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
           if (_pathname !== "/_error") {
             this.components[this.route] = {
               Component,
-              styleSheets: initialStyleSheets,
+              initial: true,
               props: initialProps,
               err,
               __N_SSG: initialProps && initialProps.__N_SSG,
@@ -1104,15 +1473,26 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             this.notify(this.components[this.route]);
             Router.events.emit("hashChangeComplete", as);
             return true;
-          } // The build manifest needs to be loaded before auto-static dynamic pages
+          }
+
+          var parsed = (0, _parseRelativeUrl.parseRelativeUrl)(url);
+          var { pathname, query } = parsed; // The build manifest needs to be loaded before auto-static dynamic pages
           // get their query parameters to allow ensuring they can be parsed properly
           // when rewritten to
 
-          var pages = await this.pageLoader.getPageList();
-          var { __rewrites: rewrites } = await this.pageLoader
-            .promisedBuildManifest;
-          var parsed = (0, _parseRelativeUrl.parseRelativeUrl)(url);
-          var { pathname, query } = parsed;
+          var pages, rewrites;
+
+          try {
+            pages = await this.pageLoader.getPageList();
+            ({ __rewrites: rewrites } = await (0,
+            _routeLoader.getClientBuildManifest)());
+          } catch (err) {
+            // If we fail to resolve the page list or client-build manifest, we must
+            // do a server-side transition:
+            window.location.href = as;
+            return false;
+          }
+
           parsed = this._resolveHref(parsed, pages);
 
           if (parsed.pathname !== pathname) {
@@ -1321,7 +1701,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
             throw err;
           }
 
-          if (PAGE_LOAD_ERROR in err || loadErrorFail) {
+          if ((0, _routeLoader.isAssetError)(err) || loadErrorFail) {
             Router.events.emit("routeChangeError", err, as); // If we can't load the page it could be one of following reasons
             //  1. Page doesn't exists
             //  2. Page does exist in a different zone
@@ -1417,12 +1797,16 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
               : false;
 
           try {
-            var cachedRouteInfo = this.components[route];
+            var existingRouteInfo = this.components[route];
 
-            if (shallow && cachedRouteInfo && this.route === route) {
-              return cachedRouteInfo;
+            if (shallow && existingRouteInfo && this.route === route) {
+              return existingRouteInfo;
             }
 
+            var cachedRouteInfo =
+              existingRouteInfo && "initial" in existingRouteInfo
+                ? undefined
+                : existingRouteInfo;
             var routeInfo = cachedRouteInfo
               ? cachedRouteInfo
               : await this.fetchComponent(route).then(res => ({
Diff for 677f882d2ed8..43ac7e320.js

Diff too large to display

Diff for main-64ea0e7..d385e738f.js

Diff too large to display

Diff for main-769277c..99.module.js
@@ -247,7 +247,7 @@
 
       var _headManager = _interopRequireDefault(__webpack_require__("DqTX"));
 
-      var _pageLoader = _interopRequireWildcard3(__webpack_require__("zmvN"));
+      var _pageLoader = _interopRequireDefault(__webpack_require__("zmvN"));
 
       var _performanceRelayer = _interopRequireDefault(
         __webpack_require__("bGXG")
@@ -262,6 +262,9 @@
       window.__NEXT_DATA__ = data;
       var version = "10.0.2-canary.9";
       exports.version = version;
+
+      var looseToArray = input => [].slice.call(input);
+
       var {
         props: hydrateProps,
         err: hydrateErr,
@@ -301,11 +304,11 @@
           normalizeLocalePath;
       }
 
-      var pageLoader = new _pageLoader.default(buildId, prefix, page);
+      var pageLoader = new _pageLoader.default(buildId, prefix);
 
       var register = _ref => {
         var [r, f] = _ref;
-        return pageLoader.registerPage(r, f);
+        return pageLoader.routeLoader.onEntrypoint(r, f);
       };
 
       if (window.__NEXT_P) {
@@ -324,7 +327,6 @@
       var router;
       exports.router = router;
       var CachedComponent;
-      var cachedStyleSheets;
       var CachedApp, onPerfEntry;
 
       class Container extends _react.default.Component {
@@ -410,7 +412,15 @@
         if (false) {
         }
 
-        var { page: app, mod } = await pageLoader.loadPage("/_app");
+        var appEntrypoint = await pageLoader.routeLoader.whenEntrypoint(
+          "/_app"
+        );
+
+        if ("error" in appEntrypoint) {
+          throw appEntrypoint.error;
+        }
+
+        var { component: app, exports: mod } = appEntrypoint;
         CachedApp = app;
 
         if (mod && mod.reportWebVitals) {
@@ -450,19 +460,23 @@
         var initialErr = hydrateErr;
 
         try {
-          ({
-            page: CachedComponent,
-            styleSheets: cachedStyleSheets
-          } = await pageLoader.loadPage(page));
+          var pageEntrypoint = // The dev server fails to serve script assets when there's a hydration
+            // error, so we need to skip waiting for the entrypoint.
+            false
+              ? undefined
+              : await pageLoader.routeLoader.whenEntrypoint(page);
+
+          if ("error" in pageEntrypoint) {
+            throw pageEntrypoint.error;
+          }
+
+          CachedComponent = pageEntrypoint.component;
 
           if (false) {
             var isValidElementType;
           }
         } catch (error) {
-          if (_pageLoader.INITIAL_CSS_LOAD_ERROR in error) {
-            throw error;
-          } // This catches errors like throwing in the top level of a module
-
+          // This catches errors like throwing in the top level of a module
           initialErr = error;
         }
 
@@ -483,20 +497,15 @@
             pageLoader,
             App: CachedApp,
             Component: CachedComponent,
-            initialStyleSheets: cachedStyleSheets,
             wrapApp,
             err: initialErr,
             isFallback: Boolean(isFallback),
-            subscription: (_ref3, App) => {
-              var { Component, styleSheets, props, err } = _ref3;
-              return render({
-                App,
-                Component,
-                styleSheets,
-                props,
-                err
-              });
-            },
+            subscription: (info, App) =>
+              render(
+                Object.assign({}, info, {
+                  App
+                })
+              ),
             locale,
             locales,
             defaultLocale
@@ -508,8 +517,8 @@
 
         var renderCtx = {
           App: CachedApp,
+          initial: true,
           Component: CachedComponent,
-          styleSheets: cachedStyleSheets,
           props: hydrateProps,
           err: initialErr
         };
@@ -561,8 +570,8 @@
         } // Make sure we log the error to the console, otherwise users can't track down issues.
 
         console.error(err);
-        return pageLoader.loadPage("/_error").then(_ref4 => {
-          var { page: ErrorComponent, styleSheets } = _ref4;
+        return pageLoader.loadPage("/_error").then(_ref3 => {
+          var { page: ErrorComponent, styleSheets } = _ref3;
           // In production we do a normal render with the `ErrorComponent` as component.
           // If we've gotten here upon initial render, we can use the props from the server.
           // Otherwise, we need to call `getInitialProps` on `App` before mounting.
@@ -594,9 +603,8 @@
             )
           );
         });
-      } // If hydrate does not exist, eg in preact.
+      }
 
-      var isInitialRender = typeof _reactDom.default.hydrate === "function";
       var reactRoot = null;
 
       function renderReactElement(reactEl, domEl) {
@@ -608,10 +616,8 @@
             performance.mark("beforeRender");
           } // The check for `.hydrate` is there to support React alternatives like preact
 
-          if (isInitialRender) {
+          if (typeof _reactDom.default.hydrate === "function") {
             _reactDom.default.hydrate(reactEl, domEl, markHydrateComplete);
-
-            isInitialRender = false;
           } else {
             _reactDom.default.render(reactEl, domEl, markRenderComplete);
           }
@@ -684,8 +690,8 @@
         ].forEach(mark => performance.clearMarks(mark));
       }
 
-      function AppContainer(_ref5) {
-        var { children } = _ref5;
+      function AppContainer(_ref4) {
+        var { children } = _ref4;
         return /*#__PURE__*/ _react.default.createElement(
           Container,
           {
@@ -724,8 +730,9 @@
         );
       };
 
-      function doRender(_ref6) {
-        var { App, Component, props, err, styleSheets } = _ref6;
+      function doRender(input) {
+        var { App, Component, props, err } = input;
+        var styleSheets = "initial" in input ? undefined : input.styleSheets;
         Component = Component || lastAppProps.Component;
         props = props || lastAppProps.props;
         var appProps = (0, _extends2.default)({}, props, {
@@ -759,23 +766,21 @@
 
         function onStart() {
           if (
-            // We can skip this during hydration. Running it wont cause any harm, but
-            // we may as well save the CPU cycles.
-            isInitialRender || // We use `style-loader` in development, so we don't need to do anything
+            !styleSheets || // We use `style-loader` in development, so we don't need to do anything
             // unless we're in production:
             false
           ) {
             return false;
           }
 
-          var currentStyleTags = (0, _pageLoader.looseToArray)(
+          var currentStyleTags = looseToArray(
             document.querySelectorAll("style[data-n-href]")
           );
           var currentHrefs = new Set(
             currentStyleTags.map(tag => tag.getAttribute("data-n-href"))
           );
-          styleSheets.forEach(_ref7 => {
-            var { href, text } = _ref7;
+          styleSheets.forEach(_ref5 => {
+            var { href, text } = _ref5;
 
             if (!currentHrefs.has(href)) {
               var styleTag = document.createElement("style");
@@ -794,11 +799,11 @@
             // unless we're in production:
             true && // We can skip this during hydration. Running it wont cause any harm, but
             // we may as well save the CPU cycles:
-            !isInitialRender && // Ensure this render was not canceled
+            styleSheets && // Ensure this render was not canceled
             !canceled
           ) {
             var desiredHrefs = new Set(styleSheets.map(s => s.href));
-            var currentStyleTags = (0, _pageLoader.looseToArray)(
+            var currentStyleTags = looseToArray(
               document.querySelectorAll("style[data-n-href]")
             );
             var currentHrefs = currentStyleTags.map(tag =>
@@ -819,8 +824,8 @@
               // This should be an invariant:
               referenceNode
             ) {
-              styleSheets.forEach(_ref8 => {
-                var { href } = _ref8;
+              styleSheets.forEach(_ref6 => {
+                var { href } = _ref6;
                 var targetTag = document.querySelector(
                   'style[data-n-href="'.concat(href, '"]')
                 );
@@ -838,11 +843,11 @@
               });
             } // Finally, clean up server rendered stylesheets:
 
-            (0, _pageLoader.looseToArray)(
-              document.querySelectorAll("link[data-n-p]")
-            ).forEach(el => {
-              el.parentNode.removeChild(el);
-            }); // Force browser to recompute layout, which should prevent a flash of
+            looseToArray(document.querySelectorAll("link[data-n-p]")).forEach(
+              el => {
+                el.parentNode.removeChild(el);
+              }
+            ); // Force browser to recompute layout, which should prevent a flash of
             // unstyled content:
 
             getComputedStyle(document.body, "height");
@@ -869,8 +874,8 @@
         return renderPromise;
       }
 
-      function Root(_ref9) {
-        var { callback, children } = _ref9;
+      function Root(_ref7) {
+        var { callback, children } = _ref7;
 
         // We use `useLayoutEffect` to guarantee the callback is executed
         // as soon as React flushes the update.
@@ -898,30 +903,6 @@
       /***/
     },
 
-    /***/ Lab5: /***/ function(module, exports, __webpack_require__) {
-      "use strict";
-
-      exports.__esModule = true;
-      exports.default = getAssetPathFromRoute; // Translates a logical route into its pages asset path (relative from a common prefix)
-      // "asset path" being its javascript file, data file, prerendered html,...
-
-      function getAssetPathFromRoute(route) {
-        var ext =
-          arguments.length > 1 && arguments[1] !== undefined
-            ? arguments[1]
-            : "";
-        var path =
-          route === "/"
-            ? "/index"
-            : /^\/index(\/|$)/.test(route)
-            ? "/index".concat(route)
-            : "".concat(route);
-        return path + ext;
-      }
-
-      /***/
-    },
-
     /***/ bGXG: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
@@ -1277,12 +1258,12 @@
     /***/ zmvN: /***/ function(module, exports, __webpack_require__) {
       "use strict";
 
+      var _interopRequireWildcard = __webpack_require__("284h");
+
       var _interopRequireDefault = __webpack_require__("TqRt");
 
       exports.__esModule = true;
-      exports.default = exports.INITIAL_CSS_LOAD_ERROR = exports.looseToArray = void 0;
-
-      var _mitt = _interopRequireDefault(__webpack_require__("dZ6Y"));
+      exports.default = void 0;
 
       var _router = __webpack_require__("elyg");
 
@@ -1294,32 +1275,7 @@
 
       var _parseRelativeUrl = __webpack_require__("hS4m");
 
-      var looseToArray = input => [].slice.call(input);
-
-      exports.looseToArray = looseToArray;
-
-      function hasRel(rel, link) {
-        try {
-          link = document.createElement("link");
-          return link.relList.supports(rel);
-        } catch (_unused) {}
-      }
-
-      function pageLoadError(route) {
-        return (0, _router.markLoadingError)(
-          new Error("Error loading ".concat(route))
-        );
-      }
-
-      var INITIAL_CSS_LOAD_ERROR = Symbol("INITIAL_CSS_LOAD_ERROR");
-      exports.INITIAL_CSS_LOAD_ERROR = INITIAL_CSS_LOAD_ERROR;
-      var relPrefetch =
-        hasRel("preload") && !hasRel("prefetch") // https://caniuse.com/#feat=link-rel-preload
-          ? // macOS and iOS (Safari does not support prefetch)
-            "preload" // https://caniuse.com/#feat=link-rel-prefetch
-          : // IE 11, Edge 12+, nearly all evergreen
-            "prefetch";
-      var hasNoModule = "noModule" in document.createElement("script");
+      var _routeLoader = _interopRequireWildcard(__webpack_require__("Nh2W"));
 
       function normalizeRoute(route) {
         if (route[0] !== "/") {
@@ -1332,75 +1288,16 @@
         return route.replace(/\/$/, "");
       }
 
-      function appendLink(href, rel, as, link) {
-        return new Promise((res, rej) => {
-          link = document.createElement("link"); // The order of property assignment here is intentional:
-
-          if (as) link.as = as;
-          link.rel = rel;
-          link.crossOrigin = "anonymous";
-          link.onload = res;
-          link.onerror = rej; // `href` should always be last:
-
-          link.href = href;
-          document.head.appendChild(link);
-        });
-      }
-
-      function loadScript(url) {
-        return new Promise((res, rej) => {
-          var script = document.createElement("script");
-
-          if (true && hasNoModule) {
-            script.type = "module";
-          }
-
-          script.crossOrigin = "anonymous";
-          script.src = url;
-          script.onload = res;
-
-          script.onerror = () => rej(pageLoadError(url));
-
-          document.body.appendChild(script);
-        });
-      }
-
       class PageLoader {
-        constructor(buildId, assetPrefix, initialPage) {
-          this.initialPage = void 0;
+        constructor(buildId, assetPrefix) {
           this.buildId = void 0;
           this.assetPrefix = void 0;
-          this.pageCache = void 0;
-          this.cssc = void 0;
-          this.pageRegisterEvents = void 0;
-          this.loadingRoutes = void 0;
-          this.promisedBuildManifest = void 0;
           this.promisedSsgManifest = void 0;
           this.promisedDevPagesManifest = void 0;
-          this.initialPage = initialPage;
+          this.routeLoader = void 0;
+          this.routeLoader = (0, _routeLoader.default)(assetPrefix);
           this.buildId = buildId;
           this.assetPrefix = assetPrefix;
-          this.pageCache = {};
-          this.cssc = {};
-          this.pageRegisterEvents = (0, _mitt.default)();
-          this.loadingRoutes = {
-            // By default these 2 pages are being loaded in the initial html
-            "/_app": true
-          }; // TODO: get rid of this limitation for rendering the error page
-
-          if (initialPage !== "/_error") {
-            this.loadingRoutes[initialPage] = true;
-          }
-
-          this.promisedBuildManifest = new Promise(resolve => {
-            if (window.__BUILD_MANIFEST) {
-              resolve(window.__BUILD_MANIFEST);
-            } else {
-              window.__BUILD_MANIFEST_CB = () => {
-                resolve(window.__BUILD_MANIFEST);
-              };
-            }
-          });
           /** @type {Promise<Set<string>>} */
 
           this.promisedSsgManifest = new Promise(resolve => {
@@ -1416,36 +1313,12 @@
 
         getPageList() {
           if (true) {
-            return this.promisedBuildManifest.then(
-              buildManifest => buildManifest.sortedPages
+            return (0, _routeLoader.getClientBuildManifest)().then(
+              manifest => manifest.sortedPages
             );
           } else {
           }
         }
-
-        fetchStyleSheet(href) {
-          if (!this.cssc[href]) {
-            this.cssc[href] = fetch(href).then(res => {
-              if (!res.ok) throw pageLoadError(href);
-              return res.text();
-            });
-          }
-
-          return this.cssc[href].then(text => ({
-            href,
-            text
-          }));
-        } // Returns a promise for the dependencies for a particular route
-
-        getDependencies(route) {
-          return this.promisedBuildManifest.then(m => {
-            return m[route]
-              ? m[route].map(url =>
-                  "".concat(this.assetPrefix, "/_next/").concat(encodeURI(url))
-                )
-              : Promise.reject(pageLoadError(route));
-          });
-        }
         /**
          * @param {string} href the route href (file-system path)
          * @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
@@ -1491,190 +1364,24 @@
         }
 
         loadPage(route) {
-          route = normalizeRoute(route);
-          return new Promise((resolve, reject) => {
-            // If there's a cached version of the page, let's use it.
-            var cachedPage = this.pageCache[route];
-
-            if (cachedPage) {
-              if ("error" in cachedPage) {
-                reject(cachedPage.error);
-              } else {
-                resolve(cachedPage);
-              }
-
-              return;
-            }
-
-            var fire = pageToCache => {
-              this.pageRegisterEvents.off(route, fire);
-              delete this.loadingRoutes[route];
-
-              if ("error" in pageToCache) {
-                reject(pageToCache.error);
-              } else {
-                resolve(pageToCache);
-              }
-            }; // Register a listener to get the page
-
-            this.pageRegisterEvents.on(route, fire);
-
-            if (!this.loadingRoutes[route]) {
-              this.loadingRoutes[route] = true;
-
-              if (true) {
-                this.getDependencies(route)
-                  .then(deps => {
-                    var pending = [];
-                    deps.forEach(d => {
-                      if (
-                        d.endsWith(".js") &&
-                        !document.querySelector('script[src^="'.concat(d, '"]'))
-                      ) {
-                        pending.push(loadScript(d));
-                      }
-
-                      if (d.endsWith(".css")) {
-                        pending.push(this.fetchStyleSheet(d));
-                      }
-                    });
-                    return Promise.all(pending);
-                  })
-                  .catch(err => {
-                    // Mark the page as failed to load if any of its required scripts
-                    // fail to load:
-                    this.pageCache[route] = {
-                      error: err
-                    };
-                    fire({
-                      error: err
-                    });
-                  });
-              } else {
-                var url, scriptRoute;
-              }
-            }
-          });
-        } // This method if called by the route code.
-
-        registerPage(route, regFn) {
-          var _this = this;
-
-          var register = async function register(styleSheets) {
-            try {
-              var mod = await regFn();
-              var pageData = {
-                page: mod.default || mod,
-                mod,
-                styleSheets
+          return this.routeLoader.loadRoute(route).then(res => {
+            if ("component" in res) {
+              return {
+                page: res.component,
+                mod: res.exports,
+                styleSheets: res.styles.map(o => ({
+                  href: o.href,
+                  text: o.content
+                }))
               };
-              _this.pageCache[route] = pageData;
-
-              _this.pageRegisterEvents.emit(route, pageData);
-            } catch (error) {
-              _this.pageCache[route] = {
-                error
-              };
-
-              _this.pageRegisterEvents.emit(route, {
-                error
-              });
             }
-          };
-
-          if (false) {
-            var check;
-          }
 
-          var isInitialLoad = route === this.initialPage;
-          var promisedDeps = // Shared styles will already be on the page:
-            route === "/_app" || false // We use `style-loader` in development:
-              ? Promise.resolve([]) // Tests that this does not block hydration:
-              : // test/integration/css-fixtures/hydrate-without-deps/
-                (isInitialLoad
-                  ? Promise.resolve(
-                      looseToArray(
-                        document.querySelectorAll("link[data-n-p]")
-                      ).map(e => e.getAttribute("href"))
-                    )
-                  : this.getDependencies(route).then(deps =>
-                      deps.filter(d => d.endsWith(".css"))
-                    )
-                ).then((
-                  cssFiles // These files should've already been fetched by now, so this
-                ) =>
-                  // should resolve instantly.
-                  Promise.all(cssFiles.map(d => this.fetchStyleSheet(d))).catch(
-                    err => {
-                      if (isInitialLoad) {
-                        Object.defineProperty(err, INITIAL_CSS_LOAD_ERROR, {});
-                      }
-
-                      throw err;
-                    }
-                  )
-                );
-          promisedDeps.then(
-            deps => register(deps),
-            error => {
-              this.pageCache[route] = {
-                error
-              };
-              this.pageRegisterEvents.emit(route, {
-                error
-              });
-            }
-          );
+            throw res.error;
+          });
         }
-        /**
-         * @param {string} route
-         * @param {boolean} [isDependency]
-         */
-
-        prefetch(route, isDependency) {
-          // https://github.com/GoogleChromeLabs/quicklink/blob/453a661fa1fa940e2d2e044452398e38c67a98fb/src/index.mjs#L115-L118
-          // License: Apache 2.0
-          var cn;
-
-          if ((cn = navigator.connection)) {
-            // Don't prefetch if using 2G or if Save-Data is enabled.
-            if (cn.saveData || /2g/.test(cn.effectiveType))
-              return Promise.resolve();
-          }
-          /** @type {string} */
-
-          var url;
 
-          if (isDependency) {
-            url = route;
-          } else {
-            if (false) {
-              var scriptRoute, ext;
-            }
-          }
-
-          return Promise.all([
-            url
-              ? url.endsWith(".css")
-                ? this.fetchStyleSheet(url)
-                : !document.querySelector(
-                    'link[rel="'
-                      .concat(relPrefetch, '"][href^="')
-                      .concat(url, '"]')
-                  ) && appendLink(url, relPrefetch, "script")
-              : 0,
-            true &&
-              !isDependency &&
-              this.getDependencies(route).then(urls =>
-                Promise.all(
-                  urls.map(dependencyUrl => this.prefetch(dependencyUrl, true))
-                )
-              )
-          ]).then(
-            // do not return any data
-            () => {}, // swallow prefetch errors
-            () => {}
-          );
+        prefetch(route) {
+          return this.routeLoader.prefetch(route);
         }
       }
Diff for index.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      href="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -82,13 +82,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-64ea0e78acdd385e738f.js"
+      src="/_next/static/chunks/main-d70a01d68da54103f8eb.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      src="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -118,13 +118,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
Diff for link.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      href="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -37,7 +37,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/pages/link-ec1fbf762c0209fe4b72.module.js"
+      href="/_next/static/chunks/pages/link-cd84dab1f87965855b1c.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -87,13 +87,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-64ea0e78acdd385e738f.js"
+      src="/_next/static/chunks/main-d70a01d68da54103f8eb.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      src="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -123,13 +123,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -147,13 +147,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/pages/link-3125d338b922d5ab1776.js"
+      src="/_next/static/chunks/pages/link-4218f7a274c65b7f23dd.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/pages/link-ec1fbf762c0209fe4b72.module.js"
+      src="/_next/static/chunks/pages/link-cd84dab1f87965855b1c.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
Diff for withRouter.html
@@ -7,7 +7,7 @@
     <noscript data-n-css="true"></noscript>
     <link
       rel="preload"
-      href="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      href="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -25,7 +25,7 @@
     />
     <link
       rel="preload"
-      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      href="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       as="script"
       crossorigin="anonymous"
     />
@@ -82,13 +82,13 @@
       src="/_next/static/chunks/polyfills-f73ba3fc145972ef83e9.js"
     ></script>
     <script
-      src="/_next/static/chunks/main-64ea0e78acdd385e738f.js"
+      src="/_next/static/chunks/main-d70a01d68da54103f8eb.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/main-769277c12cd785f40199.module.js"
+      src="/_next/static/chunks/main-def4d6976fb84f8825c7.module.js"
       async=""
       crossorigin="anonymous"
       type="module"
@@ -118,13 +118,13 @@
       type="module"
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.c3dbd33b4fb43ac7e320.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.26303b91f48a1bbfe3bc.js"
       async=""
       crossorigin="anonymous"
       nomodule=""
     ></script>
     <script
-      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.a2ba07a682852f748bcf.module.js"
+      src="/_next/static/chunks/677f882d2ed86fa3467b8979053c1a4c3f8bc4df.ea4ba74dc401a49487e6.module.js"
       async=""
       crossorigin="anonymous"
       type="module"

Serverless Mode (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
buildDuration 14.7s 14.5s -167ms
nodeModulesSize 84.8 MB 84.8 MB ⚠️ +5.05 kB
Client Bundles (main, webpack, commons) Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..01e7.js gzip 11.3 kB N/A N/A
framework.HASH.js gzip 39 kB 39 kB
main-73b1388..ecd4.js gzip 7.48 kB N/A N/A
webpack-e067..f178.js gzip 751 B 751 B
677f882d2ed8..aa5f.js gzip N/A 12.6 kB N/A
main-bc384f4..b33c.js gzip N/A 6.48 kB N/A
Overall change 58.5 kB 58.8 kB ⚠️ +347 B
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
677f882d2ed8..dule.js gzip 7.05 kB N/A N/A
framework.HA..dule.js gzip 39 kB 39 kB
main-d0704cd..dule.js gzip 6.47 kB N/A N/A
webpack-07c5..dule.js gzip 751 B 751 B
677f882d2ed8..dule.js gzip N/A 8.21 kB N/A
main-6672a7b..dule.js gzip N/A 5.58 kB N/A
Overall change 53.2 kB 53.5 kB ⚠️ +275 B
Legacy Client Bundles (polyfills)
vercel/next.js canary Timer/next.js refactor/route-loader Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-0abe79e..7616.js gzip 1.55 kB N/A N/A
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
link-d9a15a0..130a.js gzip N/A 1.57 kB N/A
Overall change 7.96 kB 7.98 kB ⚠️ +20 B
Client Pages Modern Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-d1cfb87..dule.js gzip 1.49 kB N/A N/A
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
link-973cce3..dule.js gzip N/A 1.51 kB N/A
Overall change 5.59 kB 5.6 kB ⚠️ +17 B
Client Build Manifests
vercel/next.js canary Timer/next.js refactor/route-loader Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles Overall increase ⚠️
vercel/next.js canary Timer/next.js refactor/route-loader Change
_error.js 916 kB 916 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 916 kB 916 kB
link.js 965 kB 974 kB ⚠️ +9.03 kB
routerDirect.js 958 kB 967 kB ⚠️ +8.85 kB
withRouter.js 958 kB 967 kB ⚠️ +8.85 kB
Overall change 4.72 MB 4.75 MB ⚠️ +26.7 kB
Commit: ff18a4e

@kodiakhq kodiakhq bot merged commit 0d5bf65 into vercel:canary Nov 11, 2020
@Timer Timer deleted the refactor/route-loader branch November 11, 2020 18:14
stovmascript added a commit to monitora-media/next.js that referenced this pull request Jun 3, 2021
The new route loader (vercel#19006) will timeout
loading routes after 3.8s (MS_MAX_IDLE_DELAY), but this can easily happen when
running dev on a large app.

Fixes https://github.com/vercel/next.js/issues/25676
stovmascript added a commit to monitora-media/next.js that referenced this pull request Jun 3, 2021
The new route loader (vercel#19006) will timeout
loading routes after 3.8s (MS_MAX_IDLE_DELAY), but this can easily happen when
running dev on a large app.

Fixes vercel#25675
stovmascript added a commit to monitora-media/next.js that referenced this pull request Jul 7, 2021
The new route loader (vercel#19006) will timeout
loading routes after 3.8s (MS_MAX_IDLE_DELAY), but this can easily happen when
running dev on a large app.

Fixes vercel#25675
ijjk added a commit that referenced this pull request Jul 12, 2021
* Prevent timeout when loading routes in development

The new route loader (#19006) will timeout
loading routes after 3.8s (MS_MAX_IDLE_DELAY), but this can easily happen when
running dev on a large app.

Fixes #25675

* Delay route loading timeout in development

* refactor: Integrate onBuildCallback into resolvePromiseWithTimeout

* refactor: Tweak for better dead-code elimination

Co-authored-by: JJ Kasper <[email protected]>
flybayer pushed a commit to blitz-js/next.js that referenced this pull request Aug 19, 2021
* Prevent timeout when loading routes in development

The new route loader (vercel#19006) will timeout
loading routes after 3.8s (MS_MAX_IDLE_DELAY), but this can easily happen when
running dev on a large app.

Fixes vercel#25675

* Delay route loading timeout in development

* refactor: Integrate onBuildCallback into resolvePromiseWithTimeout

* refactor: Tweak for better dead-code elimination

Co-authored-by: JJ Kasper <[email protected]>
@vercel vercel locked as resolved and limited conversation to collaborators Jan 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Verify Next.js fetches ALL of its assets across all browsers buildManifest 404 causing app failure
2 participants