diff --git a/edge-runtime/lib/response.ts b/edge-runtime/lib/response.ts index 8faf0d906a..ec0730e8ce 100644 --- a/edge-runtime/lib/response.ts +++ b/edge-runtime/lib/response.ts @@ -14,6 +14,8 @@ import { normalizeLocalePath, normalizeTrailingSlash, relativizeURL, + removeBasePath, + rewriteDataPath, } from './util.ts' export interface FetchEventResult { @@ -180,14 +182,16 @@ export const buildResponse = async ({ } if (isDataReq) { - // The rewrite target is a data request, but a middleware rewrite target is always for the page route, - // so we need to tell the server this is a data request. Setting the `x-nextjs-data` header is not enough. 🤷 - rewriteUrl.searchParams.set('__nextDataReq', '1') + rewriteUrl.pathname = rewriteDataPath({ + dataUrl: new URL(request.url).pathname, + newRoute: removeBasePath(rewriteUrl.pathname, nextConfig?.basePath), + basePath: nextConfig?.basePath, + }) + } else { + // respect trailing slash rules to prevent 308s + rewriteUrl.pathname = normalizeTrailingSlash(rewriteUrl.pathname, nextConfig?.trailingSlash) } - // respect trailing slash rules to prevent 308s - rewriteUrl.pathname = normalizeTrailingSlash(rewriteUrl.pathname, nextConfig?.trailingSlash) - const target = normalizeLocalizedTarget({ target: rewriteUrl.toString(), request, nextConfig }) if (target === request.url) { logger.withFields({ rewrite_url: rewrite }).debug('Rewrite url is same as original url') diff --git a/tests/e2e/edge-middleware.test.ts b/tests/e2e/edge-middleware.test.ts index 0eda11b710..8b32fbaa10 100644 --- a/tests/e2e/edge-middleware.test.ts +++ b/tests/e2e/edge-middleware.test.ts @@ -52,3 +52,20 @@ test('it should render OpenGraph image meta tag correctly', async ({ page, middl const size = await getImageSize(Buffer.from(imageBuffer), 'png') expect([size.width, size.height]).toEqual([1200, 630]) }) + +test('json data rewrite works', async ({ middlewarePages }) => { + const response = await fetch(`${middlewarePages.url}/_next/data/build-id/sha.json`, { + headers: { + 'x-nextjs-data': '1', + }, + }) + + expect(response.ok).toBe(true) + const body = await response.text() + + expect(body).toMatch(/^{"pageProps":/) + + const data = JSON.parse(body) + + expect(data.pageProps.message).toBeDefined() +}) diff --git a/tests/fixtures/middleware-pages/next-env.d.ts b/tests/fixtures/middleware-pages/next-env.d.ts index 4f11a03dc6..52e831b434 100644 --- a/tests/fixtures/middleware-pages/next-env.d.ts +++ b/tests/fixtures/middleware-pages/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/tests/fixtures/middleware-pages/next.config.js b/tests/fixtures/middleware-pages/next.config.js index 3cc07b88d1..c69795688a 100644 --- a/tests/fixtures/middleware-pages/next.config.js +++ b/tests/fixtures/middleware-pages/next.config.js @@ -21,6 +21,7 @@ if (platform === 'win32') { } } +/** @type {import('next').NextConfig} */ module.exports = { trailingSlash: true, output: 'standalone', diff --git a/tests/fixtures/middleware-pages/package.json b/tests/fixtures/middleware-pages/package.json index 5708c88b50..4f57aa1121 100644 --- a/tests/fixtures/middleware-pages/package.json +++ b/tests/fixtures/middleware-pages/package.json @@ -13,6 +13,8 @@ "react-dom": "18.2.0" }, "devDependencies": { - "@types/react": "18.2.47" + "@types/node": "^20.10.6", + "@types/react": "18.2.47", + "typescript": "^5.3.3" } } diff --git a/tests/fixtures/middleware-pages/tsconfig.json b/tests/fixtures/middleware-pages/tsconfig.json index 1c4f693a99..d88efa188d 100644 --- a/tests/fixtures/middleware-pages/tsconfig.json +++ b/tests/fixtures/middleware-pages/tsconfig.json @@ -11,7 +11,8 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve" + "jsx": "preserve", + "target": "ES2017" }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"] diff --git a/tests/integration/edge-handler.test.ts b/tests/integration/edge-handler.test.ts index f938a1dc12..825ed6fac1 100644 --- a/tests/integration/edge-handler.test.ts +++ b/tests/integration/edge-handler.test.ts @@ -387,8 +387,7 @@ describe('page router', () => { }) const res = await response.json() const url = new URL(res.url, 'http://n/') - expect(url.pathname).toBe('/ssr-page-2/') - expect(url.searchParams.get('__nextDataReq')).toBe('1') + expect(url.pathname).toBe('/_next/data/build-id/ssr-page-2.json') expect(res.headers['x-nextjs-data']).toBe('1') expect(response.headers.get('x-nextjs-rewrite')).toBe('/ssr-page-2/') expect(response.status).toBe(200) @@ -420,7 +419,7 @@ describe('page router', () => { expect(response.status).toBe(200) }) - test('should rewrite un-rewritten data requests to page route', async (ctx) => { + test('should NOT rewrite un-rewritten data requests to page route', async (ctx) => { await createFixture('middleware-pages', ctx) await runPlugin(ctx) const origin = await LocalServer.run(async (req, res) => { @@ -443,8 +442,7 @@ describe('page router', () => { }) const res = await response.json() const url = new URL(res.url, 'http://n/') - expect(url.pathname).toBe('/ssg/hello/') - expect(url.searchParams.get('__nextDataReq')).toBe('1') + expect(url.pathname).toBe('/_next/data/build-id/ssg/hello.json') expect(res.headers['x-nextjs-data']).toBe('1') expect(response.status).toBe(200) }) @@ -472,8 +470,7 @@ describe('page router', () => { }) const res = await response.json() const url = new URL(res.url, 'http://n/') - expect(url.pathname).toBe('/blog/first/') - expect(url.searchParams.get('__nextDataReq')).toBe('1') + expect(url.pathname).toBe('/_next/data/build-id/blog/first.json') expect(url.searchParams.get('slug')).toBe('first') expect(res.headers['x-nextjs-data']).toBe('1') expect(response.status).toBe(200) diff --git a/tests/integration/page-router.test.ts b/tests/integration/page-router.test.ts index c246a47748..eb039eb79d 100644 --- a/tests/integration/page-router.test.ts +++ b/tests/integration/page-router.test.ts @@ -95,22 +95,6 @@ test('Should revalidate path with On-demand Revalidation', a expect(dateCacheInitial).not.toBe(dateCacheRevalidated) }) -test('Should return JSON for data req to page route', async (ctx) => { - await createFixture('page-router', ctx) - await runPlugin(ctx) - - const response = await invokeFunction(ctx, { - url: '/static/revalidate-manual?__nextDataReq=1', - headers: { 'x-nextjs-data': '1' }, - }) - - expect(response.body).toMatch(/^{"pageProps":/) - - const data = JSON.parse(response.body) - - expect(data.pageProps.show).toBeDefined() -}) - test.skipIf(platform === 'win32')( 'Should set permanent "netlify-cdn-cache-control" header on fully static pages"', async (ctx) => { diff --git a/tests/utils/create-e2e-fixture.ts b/tests/utils/create-e2e-fixture.ts index 097bd4ac0d..31cf3496e1 100644 --- a/tests/utils/create-e2e-fixture.ts +++ b/tests/utils/create-e2e-fixture.ts @@ -325,6 +325,7 @@ export const fixtureFactories = { bun: () => createE2EFixture('simple', { packageManger: 'bun' }), middleware: () => createE2EFixture('middleware'), middlewareOg: () => createE2EFixture('middleware-og'), + middlewarePages: () => createE2EFixture('middleware-pages'), pageRouter: () => createE2EFixture('page-router'), pageRouterBasePathI18n: () => createE2EFixture('page-router-base-path-i18n'), turborepo: () =>