Skip to content

Commit

Permalink
Don't warn on well-known properties in searchParams (#71142)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Oct 11, 2024
1 parent b524c36 commit a48680d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 50 deletions.
76 changes: 26 additions & 50 deletions packages/next/src/server/request/search-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
describeStringPropertyAccess,
describeHasCheckingStringProperty,
throwWithStaticGenerationBailoutErrorWithDynamicError,
wellKnownProperties,
} from './utils'

export type SearchParams = { [key: string]: string | string[] | undefined }
Expand Down Expand Up @@ -588,63 +589,38 @@ function makeDynamicallyTrackedExoticSearchParamsWithDevWarnings(
})

Object.keys(underlyingSearchParams).forEach((prop) => {
switch (prop) {
// Object prototype
case 'hasOwnProperty':
case 'isPrototypeOf':
case 'propertyIsEnumerable':
case 'toString':
case 'valueOf':
case 'toLocaleString':

// Promise prototype
// fallthrough
case 'then':
case 'catch':
case 'finally':

// React Promise extension
// fallthrough
case 'status':

// Common tested properties
// fallthrough
case 'toJSON':
case '$$typeof':
case '__esModule': {
// These properties cannot be shadowed because they need to be the
// true underlying value for Promises to work correctly at runtime
unproxiedProperties.push(prop)
break
}
default: {
proxiedProperties.add(prop)
Object.defineProperty(promise, prop, {
get() {
return proxiedUnderlying[prop]
},
set(newValue) {
Object.defineProperty(promise, prop, {
value: newValue,
writable: true,
enumerable: true,
})
},
enumerable: true,
configurable: true,
})
}
if (wellKnownProperties.has(prop)) {
// These properties cannot be shadowed because they need to be the
// true underlying value for Promises to work correctly at runtime
unproxiedProperties.push(prop)
} else {
proxiedProperties.add(prop)
Object.defineProperty(promise, prop, {
get() {
return proxiedUnderlying[prop]
},
set(newValue) {
Object.defineProperty(promise, prop, {
value: newValue,
writable: true,
enumerable: true,
})
},
enumerable: true,
configurable: true,
})
}
})

const proxiedPromise = new Proxy(promise, {
get(target, prop, receiver) {
if (typeof prop === 'string') {
if (
// We are accessing a property that was proxied to the promise instance
proxiedProperties.has(prop) ||
// We are accessing a property that doesn't exist on the promise nor the underlying
Reflect.has(target, prop) === false
!wellKnownProperties.has(prop) &&
(proxiedProperties.has(prop) ||
// We are accessing a property that doesn't exist on the promise nor
// the underlying searchParams.
Reflect.has(target, prop) === false)
) {
const expression = describeStringPropertyAccess('searchParams', prop)
warnForSyncAccess(store.route, expression)
Expand Down
25 changes: 25 additions & 0 deletions packages/next/src/server/request/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,28 @@ export function throwWithStaticGenerationBailoutErrorWithDynamicError(
`Route ${route} with \`dynamic = "error"\` couldn't be rendered statically because it used ${expression}. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`
)
}

export const wellKnownProperties = new Set([
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toString',
'valueOf',
'toLocaleString',

// Promise prototype
// fallthrough
'then',
'catch',
'finally',

// React Promise extension
// fallthrough
'status',

// Common tested properties
// fallthrough
'toJSON',
'$$typeof',
'__esModule',
])

0 comments on commit a48680d

Please sign in to comment.