From e9fc73b4d3bd21ba09d95c2c51dff6e0735b8b27 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 15 Oct 2019 16:14:14 -0400 Subject: [PATCH] Temporary: SPR: Use Group Index Mapping This pull request is a temporary addition that uses the `x-now-route-params` header in serverless. This header returns the regex groups with indexes, not their named variants. As a result, we must use the getRouteMatcher utility to reverse this into Next.js' expected names. Since this got complex, I've added a test for it. We should probably remove this behavior sooner than later. --- .../webpack/loaders/next-serverless-loader.ts | 26 ++++++++++++++++++- .../integration/serverless/pages/dr/[slug].js | 5 ++++ .../integration/serverless/test/index.test.js | 23 ++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/integration/serverless/pages/dr/[slug].js diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 6d678bb227ba1..e21329d52f1df 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -70,6 +70,7 @@ const nextServerlessLoader: loader.Loader = function() { } else { return ` import {parse} from 'url' + import {parse as parseQs} from 'querystring' import {renderToHTML} from 'next/dist/next-server/server/render'; import {sendHTML} from 'next/dist/next-server/server/send-html'; ${ @@ -138,7 +139,30 @@ const nextServerlessLoader: loader.Loader = function() { // removing reliance on `req.url` and using `req.query` instead // (which is needed for "custom routes" anyway). isDynamicRoute(page) - ? `const nowParams = (req.headers && req.headers["x-now-route-params"]) ? querystring.parse(req.headers["x-now-route-params"]) : null;` + ? `const nowParams = req.headers && req.headers["x-now-route-params"] + ? getRouteMatcher( + (function() { + const { re, groups } = getRouteRegex("${page}"); + return { + re: { + // Simulate a RegExp match from the \`req.url\` input + exec: str => { + const obj = parseQs(str); + return Object.keys(obj).reduce( + (prev, key) => + Object.assign(prev, { + [key]: encodeURIComponent(obj[key]) + }), + {} + ); + } + }, + groups + }; + })() + )(req.headers["x-now-route-params"]) + : null; + ` : `const nowParams = null;` } const result = await renderToHTML(req, res, "${page}", Object.assign({}, unstable_getStaticProps ? {} : parsedUrl.query, nowParams ? nowParams : params, sprData ? { _nextSprData: '1' } : {}), renderOpts) diff --git a/test/integration/serverless/pages/dr/[slug].js b/test/integration/serverless/pages/dr/[slug].js new file mode 100644 index 0000000000000..0f29909605636 --- /dev/null +++ b/test/integration/serverless/pages/dr/[slug].js @@ -0,0 +1,5 @@ +const SlugPage = ({ query }) =>
{JSON.stringify(query)}
+ +SlugPage.getInitialProps = ({ query }) => ({ query }) + +export default SlugPage diff --git a/test/integration/serverless/test/index.test.js b/test/integration/serverless/test/index.test.js index b54b7395ec5e7..cf30d8e2b7d36 100644 --- a/test/integration/serverless/test/index.test.js +++ b/test/integration/serverless/test/index.test.js @@ -12,6 +12,7 @@ import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' +import qs from 'querystring' import fetch from 'node-fetch' const appDir = join(__dirname, '../') @@ -192,6 +193,28 @@ describe('Serverless', () => { expect(res.status).toBe(404) }) + it('should have the correct query string for a dynamic route', async () => { + const paramRaw = 'test % 123' + const param = encodeURIComponent(paramRaw) + + const html = await renderViaHTTP(appPort, `/dr/${param}`) + const $ = cheerio.load(html) + const data = JSON.parse($('#__NEXT_DATA__').html()) + + expect(data.query).toEqual({ slug: paramRaw }) + }) + + it('should have the correct query string for a spr route', async () => { + const paramRaw = 'test % 123' + const html = await fetchViaHTTP(appPort, `/dr/[slug]`, '', { + headers: { 'x-now-route-params': qs.stringify({ 1: paramRaw }) } + }).then(res => res.text()) + const $ = cheerio.load(html) + const data = JSON.parse($('#__NEXT_DATA__').html()) + + expect(data.query).toEqual({ slug: paramRaw }) + }) + describe('With basic usage', () => { it('should allow etag header support', async () => { const url = `http://localhost:${appPort}/`