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

Fixed i18n data route RegExp #55109

Merged
merged 2 commits into from Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/next/src/server/lib/router-utils/filesystem.ts
Expand Up @@ -249,7 +249,7 @@ export async function setupFsCheck(opts: {
? new RegExp(
route.dataRouteRegex.replace(
`/${escapedBuildId}/`,
`/${escapedBuildId}/(?<nextLocale>.+?)/`
`/${escapedBuildId}/(?<nextLocale>[^/]+?)/`
)
)
: new RegExp(route.dataRouteRegex),
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/lib/router-utils/setup-dev.ts
Expand Up @@ -1784,7 +1784,7 @@ async function startWatcher(opts: SetupOpts) {
? new RegExp(
route.dataRouteRegex.replace(
`/development/`,
`/development/(?<nextLocale>.+?)/`
`/development/(?<nextLocale>[^/]+?)/`
)
)
: new RegExp(route.dataRouteRegex),
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/i18n-data-route/components/page.tsx
@@ -0,0 +1,12 @@
import React from 'react'

export function Page({ page }) {
return <p data-page={page}>{page}</p>
}

export function createGetServerSideProps(page: string) {
return async function getServerSideProps(ctx) {
const output = ctx.req.headers['x-invoke-output'] ?? null
Copy link
Member Author

Choose a reason for hiding this comment

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

Internally Next.js will have this header (later stripped in #55114) that indicates the specific page that it was supposed to render.

return { props: { page, output } }
}
}
83 changes: 83 additions & 0 deletions test/e2e/i18n-data-route/i18n-data-route.test.ts
@@ -0,0 +1,83 @@
import { createNextDescribe } from 'e2e-utils'

const { i18n } = require('./next.config')

const pages = [
{ url: '/about', page: '/about', params: null },
{ url: '/blog/about', page: '/[slug]/about', params: { slug: 'blog' } },
]

function checkDataRoute(data: any, page: string) {
expect(data).toHaveProperty('pageProps')
expect(data.pageProps).toHaveProperty('page', page)
expect(data.pageProps).toHaveProperty('output', page)
}

createNextDescribe(
'i18n-data-route',
{
files: __dirname,
},
({ next }) => {
describe('with locale prefix', () => {
describe.each(i18n.locales)('/%s', (locale) => {
const prefixed = pages.map((page) => ({
...page,
url: `/${locale}${page.url}`,
}))

it.each(prefixed)(
'should render $page via $url',
async ({ url, page }) => {
const $ = await next.render$(url)
expect($('[data-page]').data('page')).toBe(page)
}
)

it.each(prefixed)(
'should serve data for $page',
async ({ url, page, params }) => {
url = `/_next/data/${next.buildId}${url}.json`
if (params) {
const query = new URLSearchParams(params)
// Ensure the query is sorted so it's deterministic.
query.sort()
url += `?${query.toString()}`
}

const res = await next.fetch(url)
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toBe('application/json')
const data = await res.json()
checkDataRoute(data, page)
}
)
})
})

describe('without locale prefix', () => {
it.each(pages)('should render $page via $url', async ({ url, page }) => {
const $ = await next.render$(url)
expect($('[data-page]').data('page')).toBe(page)
})

it.each(pages)(
'should serve data for $page',
async ({ url, page, params }) => {
url = `/_next/data/${next.buildId}/${i18n.defaultLocale}${url}.json`
if (params) {
const query = new URLSearchParams(params)
// Ensure the query is sorted so it's deterministic.
query.sort()
url += `?${query.toString()}`
}
const res = await next.fetch(url)
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toBe('application/json')
const data = await res.json()
checkDataRoute(data, page)
}
)
})
}
)
9 changes: 9 additions & 0 deletions test/e2e/i18n-data-route/next.config.js
@@ -0,0 +1,9 @@
/**
* @type {import('next').NextConfig}
*/
module.exports = {
i18n: {
locales: ['en-CA', 'fr-CA'],
defaultLocale: 'en-CA',
},
}
5 changes: 5 additions & 0 deletions test/e2e/i18n-data-route/pages/[slug]/about/index.tsx
@@ -0,0 +1,5 @@
import { Page, createGetServerSideProps } from '../../../components/page'

export default Page

export const getServerSideProps = createGetServerSideProps('/[slug]/about')
5 changes: 5 additions & 0 deletions test/e2e/i18n-data-route/pages/about/index.tsx
@@ -0,0 +1,5 @@
import { Page, createGetServerSideProps } from '../../components/page'

export default Page

export const getServerSideProps = createGetServerSideProps('/about')