| | import type { SearchParams } from '../../server/request/search-params' |
| |
|
| | import { ReflectAdapter } from '../../server/web/spec-extension/adapters/reflect' |
| | import { |
| | describeStringPropertyAccess, |
| | describeHasCheckingStringProperty, |
| | wellKnownProperties, |
| | } from '../../shared/lib/utils/reflect-utils' |
| |
|
| | interface CacheLifetime {} |
| | const CachedSearchParams = new WeakMap<CacheLifetime, Promise<SearchParams>>() |
| |
|
| | function makeUntrackedSearchParamsWithDevWarnings( |
| | underlyingSearchParams: SearchParams |
| | ): Promise<SearchParams> { |
| | const cachedSearchParams = CachedSearchParams.get(underlyingSearchParams) |
| | if (cachedSearchParams) { |
| | return cachedSearchParams |
| | } |
| |
|
| | const proxiedProperties = new Set<string>() |
| | const promise = Promise.resolve(underlyingSearchParams) |
| |
|
| | Object.keys(underlyingSearchParams).forEach((prop) => { |
| | if (wellKnownProperties.has(prop)) { |
| | |
| | |
| | } else { |
| | proxiedProperties.add(prop) |
| | } |
| | }) |
| |
|
| | const proxiedPromise = new Proxy(promise, { |
| | get(target, prop, receiver) { |
| | if (typeof prop === 'string') { |
| | if ( |
| | !wellKnownProperties.has(prop) && |
| | (proxiedProperties.has(prop) || |
| | |
| | |
| | Reflect.has(target, prop) === false) |
| | ) { |
| | const expression = describeStringPropertyAccess('searchParams', prop) |
| | warnForSyncAccess(expression) |
| | } |
| | } |
| | return ReflectAdapter.get(target, prop, receiver) |
| | }, |
| | set(target, prop, value, receiver) { |
| | if (typeof prop === 'string') { |
| | proxiedProperties.delete(prop) |
| | } |
| | return Reflect.set(target, prop, value, receiver) |
| | }, |
| | has(target, prop) { |
| | if (typeof prop === 'string') { |
| | if ( |
| | !wellKnownProperties.has(prop) && |
| | (proxiedProperties.has(prop) || |
| | |
| | |
| | Reflect.has(target, prop) === false) |
| | ) { |
| | const expression = describeHasCheckingStringProperty( |
| | 'searchParams', |
| | prop |
| | ) |
| | warnForSyncAccess(expression) |
| | } |
| | } |
| | return Reflect.has(target, prop) |
| | }, |
| | ownKeys(target) { |
| | warnForSyncSpread() |
| | return Reflect.ownKeys(target) |
| | }, |
| | }) |
| |
|
| | CachedSearchParams.set(underlyingSearchParams, proxiedPromise) |
| | return proxiedPromise |
| | } |
| |
|
| | function warnForSyncAccess(expression: string) { |
| | console.error( |
| | `A searchParam property was accessed directly with ${expression}. ` + |
| | `\`searchParams\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + |
| | `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` |
| | ) |
| | } |
| |
|
| | function warnForSyncSpread() { |
| | console.error( |
| | `The keys of \`searchParams\` were accessed directly. ` + |
| | `\`searchParams\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + |
| | `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` |
| | ) |
| | } |
| |
|
| | export function createRenderSearchParamsFromClient( |
| | underlyingSearchParams: SearchParams |
| | ): Promise<SearchParams> { |
| | return makeUntrackedSearchParamsWithDevWarnings(underlyingSearchParams) |
| | } |
| |
|