From d964154f9e39a6cb23d2a883aaebd9f86f9a21b1 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Wed, 2 Oct 2024 14:53:18 +0200 Subject: [PATCH] Allow silencing direct access of dynamic APIs to unblock internal sync --- .../webpack/plugins/define-env-plugin.ts | 4 ++ packages/next/src/server/config-schema.ts | 1 + packages/next/src/server/request/cookies.ts | 39 +++++----- .../next/src/server/request/draft-mode.ts | 20 +++--- packages/next/src/server/request/headers.ts | 39 +++++----- .../next/src/server/request/params.browser.ts | 58 +++++++++------ packages/next/src/server/request/params.ts | 72 +++++++++++-------- .../server/request/search-params.browser.ts | 42 +++++++---- .../next/src/server/request/search-params.ts | 64 +++++++++-------- 9 files changed, 205 insertions(+), 134 deletions(-) diff --git a/packages/next/src/build/webpack/plugins/define-env-plugin.ts b/packages/next/src/build/webpack/plugins/define-env-plugin.ts index 37050281d708e..e12c205c1f28a 100644 --- a/packages/next/src/build/webpack/plugins/define-env-plugin.ts +++ b/packages/next/src/build/webpack/plugins/define-env-plugin.ts @@ -266,6 +266,10 @@ export function getDefineEnv({ 'process.env.__NEXT_LINK_NO_TOUCH_START': config.experimental.linkNoTouchStart ?? false, 'process.env.__NEXT_ASSET_PREFIX': config.assetPrefix, + 'process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS': + // Internal only so untyped to avoid discovery + (config.experimental as any).internal_disableSyncDynamicAPIWarnings ?? + false, ...(isNodeOrEdgeCompilation ? { // Fix bad-actors in the npm ecosystem (e.g. `node-formidable`) diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index f489220d25e7f..df208132c65f1 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -303,6 +303,7 @@ export const configSchema: zod.ZodType = z.lazy(() => forceSwcTransforms: z.boolean().optional(), fullySpecified: z.boolean().optional(), gzipSize: z.boolean().optional(), + internal_disableSyncDynamicAPIWarnings: z.boolean().optional(), isrFlushToDisk: z.boolean().optional(), largePageDataBytes: z.number().optional(), linkNoTouchStart: z.boolean().optional(), diff --git a/packages/next/src/server/request/cookies.ts b/packages/next/src/server/request/cookies.ts index 4cc27c059a76c..40a6c318c0441 100644 --- a/packages/next/src/server/request/cookies.ts +++ b/packages/next/src/server/request/cookies.ts @@ -510,23 +510,30 @@ function describeNameArg(arg: unknown) { : '...' } -function warnForSyncIteration(route?: string) { - const prefix = route ? ` In route ${route} ` : '' - console.error( - `${prefix}cookies were iterated over. ` + - `\`cookies()\` should be awaited before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const noop = () => {} -function warnForSyncAccess(route: undefined | string, expression: string) { - const prefix = route ? ` In route ${route} a ` : 'A ' - console.error( - `${prefix}cookie property was accessed directly with \`${expression}\`. ` + - `\`cookies()\` should be awaited before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const warnForSyncIteration = process.env + .__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncIteration(route?: string) { + const prefix = route ? ` In route ${route} ` : '' + console.error( + `${prefix}cookies were iterated over. ` + + `\`cookies()\` should be awaited before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(route: undefined | string, expression: string) { + const prefix = route ? ` In route ${route} a ` : 'A ' + console.error( + `${prefix}cookie property was accessed directly with \`${expression}\`. ` + + `\`cookies()\` should be awaited before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } function polyfilledResponseCookiesIterator( this: ResponseCookies diff --git a/packages/next/src/server/request/draft-mode.ts b/packages/next/src/server/request/draft-mode.ts index d4b83f154a47a..0f02b29509768 100644 --- a/packages/next/src/server/request/draft-mode.ts +++ b/packages/next/src/server/request/draft-mode.ts @@ -163,11 +163,15 @@ class DraftMode { } } -function warnForSyncAccess(route: undefined | string, expression: string) { - const prefix = route ? ` In route ${route} a ` : 'A ' - console.error( - `${prefix}\`draftMode()\` property was accessed directly with \`${expression}\`. ` + - `\`draftMode()\` should be awaited before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/draft-mode-sync-access` - ) -} +const noop = () => {} + +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(route: undefined | string, expression: string) { + const prefix = route ? ` In route ${route} a ` : 'A ' + console.error( + `${prefix}\`draftMode()\` property was accessed directly with \`${expression}\`. ` + + `\`draftMode()\` should be awaited before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/draft-mode-sync-access` + ) + } diff --git a/packages/next/src/server/request/headers.ts b/packages/next/src/server/request/headers.ts index 3437a17fa3342..d32b58608c629 100644 --- a/packages/next/src/server/request/headers.ts +++ b/packages/next/src/server/request/headers.ts @@ -425,23 +425,30 @@ function describeNameArg(arg: unknown) { return typeof arg === 'string' ? `'${arg}'` : '...' } -function warnForSyncIteration(route?: string) { - const prefix = route ? ` In route ${route} ` : '' - console.error( - `${prefix}headers were iterated over. ` + - `\`headers()\` should be awaited before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const noop = () => {} -function warnForSyncAccess(route: undefined | string, expression: string) { - const prefix = route ? ` In route ${route} a ` : 'A ' - console.error( - `${prefix}header property was accessed directly with \`${expression}\`. ` + - `\`headers()\` should be awaited before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const warnForSyncIteration = process.env + .__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncIteration(route?: string) { + const prefix = route ? ` In route ${route} ` : '' + console.error( + `${prefix}headers were iterated over. ` + + `\`headers()\` should be awaited before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(route: undefined | string, expression: string) { + const prefix = route ? ` In route ${route} a ` : 'A ' + console.error( + `${prefix}header property was accessed directly with \`${expression}\`. ` + + `\`headers()\` should be awaited before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } type HeadersExtensions = { [K in keyof ReadonlyHeaders]: unknown diff --git a/packages/next/src/server/request/params.browser.ts b/packages/next/src/server/request/params.browser.ts index 1dcf50202f822..275b34a5888da 100644 --- a/packages/next/src/server/request/params.browser.ts +++ b/packages/next/src/server/request/params.browser.ts @@ -119,29 +119,43 @@ function makeDynamicallyTrackedExoticParamsWithDevWarnings( return proxiedPromise } -function warnForSyncAccess(expression: string) { - console.error( - `A param property was accessed directly with ${expression}. \`params\` is now a Promise and should be unwrapped with \`React.use()\` before accessing properties of the underlying params object. In this version of Next.js direct access to param properties is still supported to facilitate migration but in a future version you will be required to unwrap \`params\` with \`React.use()\`.` - ) -} +const noop = () => {} -function warnForEnumeration(missingProperties: Array) { - if (missingProperties.length) { - const describedMissingProperties = - describeListOfPropertyNames(missingProperties) - console.error( - `params are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + - `\`params\` should be unwrapped with \`React.use()\` before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } else { - console.error( - `params are being enumerated. ` + - `\`params\` should be unwrapped with \`React.use()\` before using its value. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } -} +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(expression: string) { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + console.error( + `A param property was accessed directly with ${expression}. \`params\` is now a Promise and should be unwrapped with \`React.use()\` before accessing properties of the underlying params object. In this version of Next.js direct access to param properties is still supported to facilitate migration but in a future version you will be required to unwrap \`params\` with \`React.use()\`.` + ) + } + +const warnForEnumeration = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForEnumeration(missingProperties: Array) { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + if (missingProperties.length) { + const describedMissingProperties = + describeListOfPropertyNames(missingProperties) + console.error( + `params are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + + `\`params\` should be unwrapped with \`React.use()\` before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } else { + console.error( + `params are being enumerated. ` + + `\`params\` should be unwrapped with \`React.use()\` before using its value. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + } function describeListOfPropertyNames(properties: Array) { switch (properties.length) { diff --git a/packages/next/src/server/request/params.ts b/packages/next/src/server/request/params.ts index 70d78c8790a1a..8694ea76ba5f4 100644 --- a/packages/next/src/server/request/params.ts +++ b/packages/next/src/server/request/params.ts @@ -495,36 +495,50 @@ function makeDynamicallyTrackedExoticParamsWithDevWarnings( return proxiedPromise } -function warnForSyncAccess(route: undefined | string, expression: string) { - const prefix = route ? ` In route ${route} a ` : 'A ' - console.error( - `${prefix}param property was accessed directly with ${expression}. ` + - `\`params\` should be awaited before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const noop = () => {} -function warnForEnumeration( - route: undefined | string, - missingProperties: Array -) { - const prefix = route ? ` In route ${route} ` : '' - if (missingProperties.length) { - const describedMissingProperties = - describeListOfPropertyNames(missingProperties) - console.error( - `${prefix}params are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + - `\`params\` should be awaited before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } else { - console.error( - `${prefix}params are being enumerated. ` + - `\`params\` should be awaited before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } -} +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(route: undefined | string, expression: string) { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + const prefix = route ? ` In route ${route} a ` : 'A ' + console.error( + `${prefix}param property was accessed directly with ${expression}. ` + + `\`params\` should be awaited before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + +const warnForEnumeration = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForEnumeration( + route: undefined | string, + missingProperties: Array + ) { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + const prefix = route ? ` In route ${route} ` : '' + if (missingProperties.length) { + const describedMissingProperties = + describeListOfPropertyNames(missingProperties) + console.error( + `${prefix}params are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + + `\`params\` should be awaited before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } else { + console.error( + `${prefix}params are being enumerated. ` + + `\`params\` should be awaited before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + } function describeListOfPropertyNames(properties: Array) { switch (properties.length) { diff --git a/packages/next/src/server/request/search-params.browser.ts b/packages/next/src/server/request/search-params.browser.ts index 461f3b8a5673b..57f43081cd082 100644 --- a/packages/next/src/server/request/search-params.browser.ts +++ b/packages/next/src/server/request/search-params.browser.ts @@ -119,18 +119,32 @@ function makeUntrackedExoticSearchParams( return promise } -function warnForSyncAccess(expression: string) { - console.error( - `A searchParam property was accessed directly with ${expression}. ` + - `\`searchParams\` should be unwrapped with \`React.use()\` before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const noop = () => {} -function warnForSyncSpread() { - console.error( - `The keys of \`searchParams\` were accessed directly. ` + - `\`searchParams\` should be unwrapped with \`React.use()\` before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(expression: string) { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + console.error( + `A searchParam property was accessed directly with ${expression}. ` + + `\`searchParams\` should be unwrapped with \`React.use()\` before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + +const warnForSyncSpread = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncSpread() { + if (process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS) { + return + } + + console.error( + `The keys of \`searchParams\` were accessed directly. ` + + `\`searchParams\` should be unwrapped with \`React.use()\` before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } diff --git a/packages/next/src/server/request/search-params.ts b/packages/next/src/server/request/search-params.ts index 6aa5dd7871513..f79abc1acd7aa 100644 --- a/packages/next/src/server/request/search-params.ts +++ b/packages/next/src/server/request/search-params.ts @@ -657,36 +657,42 @@ function makeDynamicallyTrackedExoticSearchParamsWithDevWarnings( return proxiedPromise } -function warnForSyncAccess(route: undefined | string, expression: string) { - const prefix = route ? ` In route ${route} a ` : 'A ' - console.error( - `${prefix}searchParam property was accessed directly with ${expression}. ` + - `\`searchParams\` should be awaited before accessing properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) -} +const noop = () => {} + +const warnForSyncAccess = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForSyncAccess(route: undefined | string, expression: string) { + const prefix = route ? ` In route ${route} a ` : 'A ' + console.error( + `${prefix}searchParam property was accessed directly with ${expression}. ` + + `\`searchParams\` should be awaited before accessing properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } -function warnForEnumeration( - route: undefined | string, - missingProperties: Array -) { - const prefix = route ? ` In route ${route} ` : '' - if (missingProperties.length) { - const describedMissingProperties = - describeListOfPropertyNames(missingProperties) - console.error( - `${prefix}searchParams are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + - `\`searchParams\` should be awaited before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } else { - console.error( - `${prefix}searchParams are being enumerated. ` + - `\`searchParams\` should be awaited before accessing its properties. ` + - `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` - ) - } -} +const warnForEnumeration = process.env.__NEXT_DISABLE_SYNC_DYNAMIC_API_WARNINGS + ? noop + : function warnForEnumeration( + route: undefined | string, + missingProperties: Array + ) { + const prefix = route ? ` In route ${route} ` : '' + if (missingProperties.length) { + const describedMissingProperties = + describeListOfPropertyNames(missingProperties) + console.error( + `${prefix}searchParams are being enumerated incompletely missing these properties: ${describedMissingProperties}. ` + + `\`searchParams\` should be awaited before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } else { + console.error( + `${prefix}searchParams are being enumerated. ` + + `\`searchParams\` should be awaited before accessing its properties. ` + + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis` + ) + } + } function describeListOfPropertyNames(properties: Array) { switch (properties.length) {