Skip to content

Commit cce9f0d

Browse files
authored
fix(metadata): align metadata suffix hash between turbopack (#57544)
### What? Wraps up metadata-dynamic-routes tests fixes for the turbopack. There is 1 remaining failing test due to lacks of supporting `import.meta.url` which need to be addressed separately. I spent amount of time why turbopack cannot find the route for the dynamic metadata for a certain route. In the end, found there are mismatching expectations for the route due to different hash for the certain route. We do use the same djb2 hash between next.js and turbopack both, so it was quite confusing why we don't get deterministic hash. After trying some experiments, found out root cause was how 2 different runtimes handle overflow for given type of numbers. In rust + turbpack we use u32 and do 32-bit hash calculation for given string, while in js we implicitly used number type as is, in result overflow occurs with default 53-bit float. Originally I tried to adjust hash in turbopack side to preserve js hash as-is, but so far I found it was non trivial to do so as rust there's no out of the box types we can coerce to the js number type. In result, unlike other fixes in turbopack this PR changes how js hash is being generated. I hope this woulndn't be a breaking changes; expect so since this is a metadata specific hash that we do not have written spec for it. Closes WEB-1890
1 parent 4df888a commit cce9f0d

File tree

4 files changed

+23
-16
lines changed

4 files changed

+23
-16
lines changed

packages/next-swc/crates/next-core/src/next_app/metadata/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ fn djb2_hash(str: &str) -> u32 {
258258
})
259259
}
260260

261-
// this is here to mirror next.js behaviour.
261+
// this is here to mirror next.js behaviour (`toString(36).slice(0, 6)`)
262262
fn format_radix(mut x: u32, radix: u32) -> String {
263263
let mut result = vec![];
264264

@@ -273,7 +273,8 @@ fn format_radix(mut x: u32, radix: u32) -> String {
273273
}
274274
}
275275

276-
result.into_iter().rev().collect()
276+
result.reverse();
277+
result[..6].iter().collect()
277278
}
278279

279280
/// If there's special convention like (...) or @ in the page path,

packages/next/src/shared/lib/hash.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
// http://www.cse.yorku.ca/~oz/hash.html
2+
// More specifically, 32-bit hash via djbxor
3+
// (ref: https://gist.github.com/eplawless/52813b1d8ad9af510d85?permalink_comment_id=3367765#gistcomment-3367765)
4+
// This is due to number type differences between rust for turbopack to js number types,
5+
// where rust does not have easy way to repreesnt js's 53-bit float number type for the matching
6+
// overflow behavior. This is more `correct` in terms of having canonical hash across different runtime / implementation
7+
// as can gaurantee determinstic output from 32bit hash.
28
export function djb2Hash(str: string) {
39
let hash = 5381
410
for (let i = 0; i < str.length; i++) {
511
const char = str.charCodeAt(i)
6-
hash = (hash << 5) + hash + char
12+
hash = ((hash << 5) + hash + char) & 0xffffffff
713
}
8-
return Math.abs(hash)
14+
return hash >>> 0
915
}
1016

1117
export function hexHash(str: string) {

test/e2e/app-dir/metadata-dynamic-routes/index.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const CACHE_HEADERS = {
88
REVALIDATE: 'public, max-age=0, must-revalidate',
99
}
1010

11-
const hashRegex = /\?\w+$/
11+
const hashRegex = /\?\w+/
1212

1313
createNextDescribe(
1414
'app dir - metadata dynamic routes',
@@ -160,12 +160,12 @@ createNextDescribe(
160160
// slug is id param from generateImageMetadata
161161
expect(iconUrls).toMatchObject([
162162
{
163-
href: '/dynamic/big/icon-48jo90/small',
163+
href: '/dynamic/big/icon-ahg52g/small',
164164
sizes: '48x48',
165165
type: 'image/png',
166166
},
167167
{
168-
href: '/dynamic/big/icon-48jo90/medium',
168+
href: '/dynamic/big/icon-ahg52g/medium',
169169
sizes: '72x72',
170170
type: 'image/png',
171171
},
@@ -183,12 +183,12 @@ createNextDescribe(
183183
// slug is index by default
184184
expect(appleTouchIconUrls).toEqual([
185185
{
186-
href: '/dynamic/big/apple-icon-48jo90/0',
186+
href: '/dynamic/big/apple-icon-ahg52g/0',
187187
sizes: '48x48',
188188
type: 'image/png',
189189
},
190190
{
191-
href: '/dynamic/big/apple-icon-48jo90/1',
191+
href: '/dynamic/big/apple-icon-ahg52g/1',
192192
sizes: '64x64',
193193
type: 'image/png',
194194
},
@@ -551,8 +551,8 @@ createNextDescribe(
551551
// dynamic
552552
'/gsp/sitemap/[__metadata_id__]/route':
553553
'app/gsp/sitemap/[__metadata_id__]/route.js',
554-
'/(group)/dynamic/[size]/apple-icon-48jo90/[[...__metadata_id__]]/route':
555-
'app/(group)/dynamic/[size]/apple-icon-48jo90/[[...__metadata_id__]]/route.js',
554+
'/(group)/dynamic/[size]/apple-icon-ahg52g/[[...__metadata_id__]]/route':
555+
'app/(group)/dynamic/[size]/apple-icon-ahg52g/[[...__metadata_id__]]/route.js',
556556
})
557557
})
558558

test/turbopack-tests-manifest.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3314,13 +3314,13 @@
33143314
"app dir - metadata dynamic routes social image routes should support generate multi sitemaps with generateSitemaps",
33153315
"app dir - metadata dynamic routes text routes should handle robots.[ext] dynamic routes",
33163316
"app dir - metadata dynamic routes text routes should handle sitemap.[ext] dynamic routes",
3317-
"app dir - metadata dynamic routes text routes should not throw if client components are imported but not used"
3317+
"app dir - metadata dynamic routes text routes should not throw if client components are imported but not used",
3318+
"app dir - metadata dynamic routes social image routes should fill params into routes groups url of static images",
3319+
"app dir - metadata dynamic routes social image routes should support params as argument in dynamic routes",
3320+
"app dir - metadata dynamic routes should generate unique path for image routes under group routes"
33183321
],
33193322
"failed": [
3320-
"app dir - metadata dynamic routes should generate unique path for image routes under group routes",
3321-
"app dir - metadata dynamic routes social image routes should fill params into routes groups url of static images",
3322-
"app dir - metadata dynamic routes social image routes should handle custom fonts in both edge and nodejs runtime",
3323-
"app dir - metadata dynamic routes social image routes should support params as argument in dynamic routes"
3323+
"app dir - metadata dynamic routes social image routes should handle custom fonts in both edge and nodejs runtime"
33243324
],
33253325
"pending": [],
33263326
"flakey": [],

0 commit comments

Comments
 (0)