diff --git a/lib/constants/regex.js b/lib/constants/regex.js new file mode 100644 index 0000000..98a6a96 --- /dev/null +++ b/lib/constants/regex.js @@ -0,0 +1,9 @@ +const CATCH_ALL_REGEX = /\/\[\.{3}(.*)\](.json)?$/; +const OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)\]{2}(.json)?$/; +const DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)\]/g; + +module.exports = { + CATCH_ALL_REGEX, + OPTIONAL_CATCH_ALL_REGEX, + DYNAMIC_PARAMETER_REGEX, +}; diff --git a/lib/helpers/getNetlifyRoutes.js b/lib/helpers/getNetlifyRoutes.js index 16ee912..3bc0f8e 100644 --- a/lib/helpers/getNetlifyRoutes.js +++ b/lib/helpers/getNetlifyRoutes.js @@ -1,9 +1,11 @@ // Adapted from @sls-next/lambda-at-edge (v1.2.0-alpha.3) // See: https://github.com/danielcondemarin/serverless-next.js/blob/57142970b08e6bc3faf0fc70749b3b0501ad7869/packages/lambda-at-edge/src/lib/expressifyDynamicRoute.ts#L4 -const CATCH_ALL_REGEX = /\/\[\.{3}(.*)\](.json)?$/; -const OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)\]{2}(.json)?$/; -const DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)\]/g; +const { + CATCH_ALL_REGEX, + OPTIONAL_CATCH_ALL_REGEX, + DYNAMIC_PARAMETER_REGEX, +} = require("../constants/regex"); // Convert dynamic NextJS routes into their Netlify-equivalent // Note that file endings (.json) must be removed for catch-all and optional diff --git a/lib/helpers/isRootCatchAllRedirect.js b/lib/helpers/isRootCatchAllRedirect.js new file mode 100644 index 0000000..8363f69 --- /dev/null +++ b/lib/helpers/isRootCatchAllRedirect.js @@ -0,0 +1,5 @@ +// Return true if the redirect is a root level catch-all +// (e.g., /[[...slug]]) +const isRootCatchAllRedirect = (redirect) => redirect.startsWith("/*"); + +module.exports = isRootCatchAllRedirect; diff --git a/lib/steps/setupRedirects.js b/lib/steps/setupRedirects.js index bfa1d59..efd4d41 100644 --- a/lib/steps/setupRedirects.js +++ b/lib/steps/setupRedirects.js @@ -4,6 +4,7 @@ const { logTitle, logItem } = require("../helpers/logger"); const { NETLIFY_PUBLISH_PATH, CUSTOM_REDIRECTS_PATH } = require("../config"); const getSortedRoutes = require("../helpers/getSortedRoutes"); const getNetlifyRoutes = require("../helpers/getNetlifyRoutes"); +const isRootCatchAllRedirect = require("../helpers/isRootCatchAllRedirect"); // Setup _redirects file that routes all requests to the appropriate location, // such as one of the Netlify functions or one of the static files. @@ -14,7 +15,7 @@ const setupRedirects = () => { const redirects = []; if (existsSync(CUSTOM_REDIRECTS_PATH)) { logItem("# Prepending custom redirects"); - redirects.push(readFileSync(CUSTOM_REDIRECTS_PATH)); + redirects.push(readFileSync(CUSTOM_REDIRECTS_PATH, "utf8")); } // Collect redirects for NextJS pages @@ -50,6 +51,16 @@ const setupRedirects = () => { }); }); + // This takes care of this issue: https://github.com/netlify/next-on-netlify/issues/43 + // where the page chunk for a root level catch-all is served incorrectly to the client. + // NOTE: Netlify is also investigating this issue internally. + const hasRootCatchAll = redirects.some(isRootCatchAllRedirect); + if (hasRootCatchAll) { + const rootCatchAllIndex = redirects.findIndex(isRootCatchAllRedirect); + // Add general "no-op" redirect before the root catch-all redirect + redirects.splice(rootCatchAllIndex, 0, "/_next/* /_next/:splat 200"); + } + // Write redirects to _redirects file writeFileSync(join(NETLIFY_PUBLISH_PATH, "_redirects"), redirects.join("\n")); }; diff --git a/package.json b/package.json index d3fae1d..b3b5440 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ }, "husky": { "hooks": { - "pre-commit": "npm run format" + "pre-commit": "prettier --check ." } } } diff --git a/tests/__snapshots__/optionalCatchAll.test.js.snap b/tests/__snapshots__/optionalCatchAll.test.js.snap index c3ab72b..5b991a2 100644 --- a/tests/__snapshots__/optionalCatchAll.test.js.snap +++ b/tests/__snapshots__/optionalCatchAll.test.js.snap @@ -7,5 +7,6 @@ exports[`Routing creates Netlify redirects 1`] = ` /_next/data/%BUILD_ID%/* /.netlify/functions/next_all 200 /page /.netlify/functions/next_page 200 / /.netlify/functions/next_all 200 +/_next/* /_next/:splat 200 /* /.netlify/functions/next_all 200" `;