Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/run/handlers/cache.cts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
) {
// pages router doesn't have cache tags headers in PAGE cache value
// so we need to generate appropriate cache tags for it
const cacheTags = [`_N_T_${key === '/index' ? '/' : key}`]
// encode here to deal with non ASCII characters in the key

const cacheTags = [`_N_T_${key === '/index' ? '/' : encodeURI(key)}`]
requestContext.responseCacheTags = cacheTags
}
}
Expand Down Expand Up @@ -341,7 +343,8 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
if (data?.kind === 'PAGE' || data?.kind === 'PAGES') {
const requestContext = getRequestContext()
if (requestContext?.didPagesRouterOnDemandRevalidate) {
const tag = `_N_T_${key === '/index' ? '/' : key}`
// encode here to deal with non ASCII characters in the key
const tag = `_N_T_${key === '/index' ? '/' : encodeURI(key)}`
getLogger().debug(`Purging CDN cache for: [${tag}]`)
requestContext.trackBackgroundWork(
purgeCache({ tags: [tag] }).catch((error) => {
Expand Down
62 changes: 52 additions & 10 deletions tests/e2e/page-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,28 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => {
revalidateApiBasePath: '/api/revalidate-no-await',
expectedH1Content: 'Product not-prerendered-and-not-awaited-revalidation',
},
{
label:
'prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
prerendered: true,
pagePath: '/products/事前レンダリング',
revalidateApiBasePath: '/api/revalidate',
expectedH1Content: 'Product 事前レンダリング',
},
{
label:
'not prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
prerendered: false,
pagePath: '/products/事前レンダリングされていない',
revalidateApiBasePath: '/api/revalidate',
expectedH1Content: 'Product 事前レンダリングされていない',
},
]) {
test(label, async ({ page, pollUntilHeadersMatch, pageRouter }) => {
// in case there is retry or some other test did hit that path before
// we want to make sure that cdn cache is not warmed up
const purgeCdnCache = await page.goto(
new URL(`/api/purge-cdn?path=${pagePath}`, pageRouter.url).href,
new URL(`/api/purge-cdn?path=${encodeURI(pagePath)}`, pageRouter.url).href,
)
expect(purgeCdnCache?.status()).toBe(200)

Expand All @@ -110,7 +126,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => {
const headers1 = response1?.headers() || {}
expect(response1?.status()).toBe(200)
expect(headers1['x-nextjs-cache']).toBeUndefined()
expect(headers1['netlify-cache-tag']).toBe(`_n_t_${pagePath}`)
expect(headers1['netlify-cache-tag']).toBe(`_n_t_${encodeURI(pagePath).toLowerCase()}`)
expect(headers1['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down Expand Up @@ -138,7 +154,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => {
const headers1Json = response1Json?.headers() || {}
expect(response1Json?.status()).toBe(200)
expect(headers1Json['x-nextjs-cache']).toBeUndefined()
expect(headers1Json['netlify-cache-tag']).toBe(`_n_t_${pagePath}`)
expect(headers1Json['netlify-cache-tag']).toBe(`_n_t_${encodeURI(pagePath).toLowerCase()}`)
expect(headers1Json['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down Expand Up @@ -459,14 +475,32 @@ test.describe('Page Router with basePath and i18n', () => {
revalidateApiBasePath: '/api/revalidate-no-await',
expectedH1Content: 'Product not-prerendered-and-not-awaited-revalidation',
},
{
label:
'prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
prerendered: true,
pagePath: '/products/事前レンダリング',
revalidateApiBasePath: '/api/revalidate',
expectedH1Content: 'Product 事前レンダリング',
},
{
label:
'not prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
prerendered: false,
pagePath: '/products/事前レンダリングされていない',
revalidateApiBasePath: '/api/revalidate',
expectedH1Content: 'Product 事前レンダリングされていない',
},
]) {
test.describe(label, () => {
test(`default locale`, async ({ page, pollUntilHeadersMatch, pageRouterBasePathI18n }) => {
// in case there is retry or some other test did hit that path before
// we want to make sure that cdn cache is not warmed up
const purgeCdnCache = await page.goto(
new URL(`/base/path/api/purge-cdn?path=/en${pagePath}`, pageRouterBasePathI18n.url)
.href,
new URL(
`/base/path/api/purge-cdn?path=/en${encodeURI(pagePath)}`,
pageRouterBasePathI18n.url,
).href,
)
expect(purgeCdnCache?.status()).toBe(200)

Expand Down Expand Up @@ -494,7 +528,9 @@ test.describe('Page Router with basePath and i18n', () => {
const headers1ImplicitLocale = response1ImplicitLocale?.headers() || {}
expect(response1ImplicitLocale?.status()).toBe(200)
expect(headers1ImplicitLocale['x-nextjs-cache']).toBeUndefined()
expect(headers1ImplicitLocale['netlify-cache-tag']).toBe(`_n_t_/en${pagePath}`)
expect(headers1ImplicitLocale['netlify-cache-tag']).toBe(
`_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
)
expect(headers1ImplicitLocale['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand All @@ -520,7 +556,9 @@ test.describe('Page Router with basePath and i18n', () => {
const headers1ExplicitLocale = response1ExplicitLocale?.headers() || {}
expect(response1ExplicitLocale?.status()).toBe(200)
expect(headers1ExplicitLocale['x-nextjs-cache']).toBeUndefined()
expect(headers1ExplicitLocale['netlify-cache-tag']).toBe(`_n_t_/en${pagePath}`)
expect(headers1ExplicitLocale['netlify-cache-tag']).toBe(
`_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
)
expect(headers1ExplicitLocale['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down Expand Up @@ -552,7 +590,9 @@ test.describe('Page Router with basePath and i18n', () => {
const headers1Json = response1Json?.headers() || {}
expect(response1Json?.status()).toBe(200)
expect(headers1Json['x-nextjs-cache']).toBeUndefined()
expect(headers1Json['netlify-cache-tag']).toBe(`_n_t_/en${pagePath}`)
expect(headers1Json['netlify-cache-tag']).toBe(
`_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
)
expect(headers1Json['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down Expand Up @@ -870,7 +910,7 @@ test.describe('Page Router with basePath and i18n', () => {
const headers1 = response1?.headers() || {}
expect(response1?.status()).toBe(200)
expect(headers1['x-nextjs-cache']).toBeUndefined()
expect(headers1['netlify-cache-tag']).toBe(`_n_t_/de${pagePath}`)
expect(headers1['netlify-cache-tag']).toBe(`_n_t_/de${encodeURI(pagePath).toLowerCase()}`)
expect(headers1['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down Expand Up @@ -899,7 +939,9 @@ test.describe('Page Router with basePath and i18n', () => {
const headers1Json = response1Json?.headers() || {}
expect(response1Json?.status()).toBe(200)
expect(headers1Json['x-nextjs-cache']).toBeUndefined()
expect(headers1Json['netlify-cache-tag']).toBe(`_n_t_/de${pagePath}`)
expect(headers1Json['netlify-cache-tag']).toBe(
`_n_t_/de${encodeURI(pagePath).toLowerCase()}`,
)
expect(headers1Json['netlify-cdn-cache-control']).toBe(
's-maxage=31536000, stale-while-revalidate=31536000, durable',
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ export async function getStaticProps({ params }) {
}
}

export const getStaticPaths = () => {
/** @type {import('next').GetStaticPaths} */
export const getStaticPaths = ({ locales }) => {
return {
paths: [
{
params: {
slug: 'prerendered',
},
locale: 'en',
},
{
params: {
slug: 'prerendered',
// Japanese prerendered (non-ascii)
slug: '事前レンダリング',
},
locale: 'de',
},
],
].flatMap((pathDescription) => locales.map((locale) => ({ ...pathDescription, locale }))),
fallback: 'blocking', // false or "blocking"
}
}
Expand Down
6 changes: 6 additions & 0 deletions tests/fixtures/page-router/pages/products/[slug].js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export const getStaticPaths = () => {
slug: 'prerendered',
},
},
{
params: {
// Japanese prerendered (non-ascii)
slug: '事前レンダリング',
},
},
],
fallback: 'blocking', // false or "blocking"
}
Expand Down
1 change: 1 addition & 0 deletions tests/integration/cache-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('page router', () => {
// the real key is much longer and ends in a hash, but we only assert on the first 50 chars to make it easier
'/products/an-incredibly-long-product-',
'/products/prerendered',
'/products/事前レンダリング',
'/static/revalidate-automatic',
'/static/revalidate-manual',
'/static/revalidate-slow',
Expand Down
1 change: 1 addition & 0 deletions tests/integration/static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ test<FixtureTestContext>('requesting a non existing page route that needs to be
expect(entries.map(({ key }) => decodeBlobKey(key.substring(0, 50))).sort()).toEqual([
'/products/an-incredibly-long-product-',
'/products/prerendered',
'/products/事前レンダリング',
'/static/revalidate-automatic',
'/static/revalidate-manual',
'/static/revalidate-slow',
Expand Down
Loading