mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Split remaining "client-node"-only modules into .browser variants (#95366)
Closes https://linear.app/vercel/issue/NAR-860/ Resolves all recent regressions of introducing `next/src/server` into client-browser bundles.
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
#[rustfmt::skip]
|
||||
pub static BROWSER_VARIANT_MODULES: &[&str] = &[
|
||||
"client/components/client-boundary-params",
|
||||
"client/components/instant-samples",
|
||||
"client/components/instant-validation/impl",
|
||||
"client/components/navigation-dynamic-rendering",
|
||||
"client/components/server-async-storage",
|
||||
"client/components/unstable-rethrow",
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// `packages/next/dist` (extension omitted).
|
||||
export const browserVariantModules = [
|
||||
'client/components/client-boundary-params',
|
||||
'client/components/instant-samples',
|
||||
'client/components/instant-validation/impl',
|
||||
'client/components/navigation-dynamic-rendering',
|
||||
'client/components/server-async-storage',
|
||||
'client/components/unstable-rethrow',
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const instrumentParamsForClientValidation = undefined
|
||||
export const expectCompleteParamsInClientValidation = undefined
|
||||
export const instrumentSearchParamsForClientValidation = undefined
|
||||
+6
-6
@@ -1,13 +1,13 @@
|
||||
import type { Params } from '../../request/params'
|
||||
import type { ReadonlyURLSearchParams } from '../../../client/components/readonly-url-search-params'
|
||||
import { workUnitAsyncStorage } from '../work-unit-async-storage.external'
|
||||
import { workAsyncStorage } from '../work-async-storage.external'
|
||||
import type { Params } from '../../server/request/params'
|
||||
import type { ReadonlyURLSearchParams } from './readonly-url-search-params'
|
||||
import { workUnitAsyncStorage } from '../../server/app-render/work-unit-async-storage.external'
|
||||
import { workAsyncStorage } from '../../server/app-render/work-async-storage.external'
|
||||
import {
|
||||
createExhaustiveParamsProxy,
|
||||
createExhaustiveURLSearchParamsProxy,
|
||||
trackMissingSampleErrorAndThrow,
|
||||
} from './instant-samples'
|
||||
import { InstantValidationError } from './instant-validation-error'
|
||||
} from '../../server/app-render/instant-validation/instant-samples'
|
||||
import { InstantValidationError } from '../../server/app-render/instant-validation/instant-validation-error'
|
||||
|
||||
export function instrumentParamsForClientValidation<TPArams extends Params>(
|
||||
underlyingParams: TPArams
|
||||
@@ -1,29 +1,9 @@
|
||||
'use client'
|
||||
|
||||
// This facade ensures that the boundary code is DCE'd in browser bundles.
|
||||
//
|
||||
// It also exists to satisfy `browser-chunks.test.ts`, which looks for
|
||||
// references to code in `packages/next/src/server` in browser bundles and errors if it finds any.
|
||||
// A "use client" module seems to always have always have an entry in the browser bundle,
|
||||
// so this module cannot be colocated with the rest of the instant validation code,
|
||||
// because it ends up looking like it's importing server code in the browser
|
||||
// even though all the server code inside is actually DCE'd.
|
||||
|
||||
const {
|
||||
InstantValidationBoundaryContext,
|
||||
PlaceValidationBoundaryBelowThisLevel,
|
||||
RenderValidationBoundaryAtThisLevel,
|
||||
SlotMarker,
|
||||
} =
|
||||
typeof window === 'undefined' && process.env.__NEXT_CACHE_COMPONENTS
|
||||
? // TODO(browser-variant): migrate to a .ts/.browser.ts split so the browser bundle drops the server branch; see scripts/generate-browser-variant-aliases.mjs
|
||||
// ast-grep-ignore: no-typeof-window-require-tsx
|
||||
(require('../../../server/app-render/instant-validation/boundary-impl') as typeof import('../../../server/app-render/instant-validation/boundary-impl'))
|
||||
: ({} as typeof import('../../../server/app-render/instant-validation/boundary-impl'))
|
||||
|
||||
// We can't fork a `use client` boundary based on node-client vs browser-client.
|
||||
// We need to fork one level deeper.
|
||||
export {
|
||||
InstantValidationBoundaryContext,
|
||||
PlaceValidationBoundaryBelowThisLevel,
|
||||
RenderValidationBoundaryAtThisLevel,
|
||||
SlotMarker,
|
||||
}
|
||||
} from './impl'
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const InstantValidationBoundaryContext = null
|
||||
export const PlaceValidationBoundaryBelowThisLevel = null
|
||||
export const RenderValidationBoundaryAtThisLevel = null
|
||||
export const SlotMarker = null
|
||||
@@ -0,0 +1,6 @@
|
||||
export {
|
||||
InstantValidationBoundaryContext,
|
||||
PlaceValidationBoundaryBelowThisLevel,
|
||||
RenderValidationBoundaryAtThisLevel,
|
||||
SlotMarker,
|
||||
} from '../../../server/app-render/instant-validation/boundary-impl'
|
||||
@@ -33,6 +33,10 @@ import { ErrorBoundary } from './error-boundary'
|
||||
import { disableSmoothScrollDuringRouteTransition } from '../../shared/lib/router/utils/disable-smooth-scroll'
|
||||
import { RedirectBoundary } from './redirect-boundary'
|
||||
import { HTTPAccessFallbackBoundary } from './http-access-fallback/error-boundary'
|
||||
import {
|
||||
InstantValidationBoundaryContext,
|
||||
RenderValidationBoundaryAtThisLevel,
|
||||
} from './instant-validation/boundary'
|
||||
import { createRouterCacheKey } from './router-reducer/create-router-cache-key'
|
||||
import {
|
||||
useRouterBFCache,
|
||||
@@ -670,10 +674,6 @@ export default function OuterLayoutRouter({
|
||||
|
||||
let maybeValidationBoundaryId: string | null = null
|
||||
if (typeof window === 'undefined' && process.env.__NEXT_CACHE_COMPONENTS) {
|
||||
const { InstantValidationBoundaryContext } =
|
||||
// TODO(browser-variant): migrate to a .ts/.browser.ts split so the browser bundle drops the server branch; see scripts/generate-browser-variant-aliases.mjs
|
||||
// ast-grep-ignore: no-typeof-window-require-tsx
|
||||
require('./instant-validation/boundary') as typeof import('./instant-validation/boundary')
|
||||
maybeValidationBoundaryId = use(InstantValidationBoundaryContext)
|
||||
}
|
||||
|
||||
@@ -812,10 +812,6 @@ export default function OuterLayoutRouter({
|
||||
process.env.__NEXT_CACHE_COMPONENTS &&
|
||||
typeof maybeValidationBoundaryId === 'string'
|
||||
) {
|
||||
const { RenderValidationBoundaryAtThisLevel } =
|
||||
// TODO(browser-variant): migrate to a .ts/.browser.ts split so the browser bundle drops the server branch; see scripts/generate-browser-variant-aliases.mjs
|
||||
// ast-grep-ignore: no-typeof-window-require-tsx
|
||||
require('./instant-validation/boundary') as typeof import('./instant-validation/boundary')
|
||||
templateValue = (
|
||||
<RenderValidationBoundaryAtThisLevel id={maybeValidationBoundaryId}>
|
||||
{templateValue}
|
||||
|
||||
@@ -27,12 +27,9 @@ const {
|
||||
instrumentParamsForClientValidation,
|
||||
instrumentSearchParamsForClientValidation,
|
||||
expectCompleteParamsInClientValidation,
|
||||
} =
|
||||
typeof window === 'undefined' && process.env.__NEXT_CACHE_COMPONENTS
|
||||
? // TODO(browser-variant): migrate to a .ts/.browser.ts split so the browser bundle drops the server branch; see scripts/generate-browser-variant-aliases.mjs
|
||||
// ast-grep-ignore: no-typeof-window-require
|
||||
(require('../../server/app-render/instant-validation/instant-samples-client') as typeof import('../../server/app-render/instant-validation/instant-samples-client'))
|
||||
: {}
|
||||
} = process.env.__NEXT_CACHE_COMPONENTS
|
||||
? (require('./instant-samples') as typeof import('./instant-samples'))
|
||||
: {}
|
||||
|
||||
/**
|
||||
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
|
||||
|
||||
@@ -45,13 +45,11 @@ describe('browser-chunks', () => {
|
||||
)
|
||||
})
|
||||
|
||||
// These snapshots document which matching modules currently reach browser
|
||||
// chunks. Some of these we don't intend to act on yet, so we snapshot the
|
||||
// normalized paths (rather than hard-fail) to surface regressions on review.
|
||||
it('must not bundle any server modules into browser chunks', () => {
|
||||
const serverSources = Array.from(
|
||||
new Set(
|
||||
sources
|
||||
// normalizing in case we regress and want to keep track of the regression
|
||||
.map(normalizeSource)
|
||||
.filter(
|
||||
(source) =>
|
||||
@@ -63,46 +61,7 @@ describe('browser-chunks', () => {
|
||||
)
|
||||
).sort()
|
||||
|
||||
// This set varies along two axes, so snapshot each combination separately
|
||||
// rather than forcing them to agree:
|
||||
// - bundler: webpack's browser chunks contain none of these; Turbopack
|
||||
// still pulls in a set we haven't acted on yet.
|
||||
// - cache components: enabling it pulls additional server modules
|
||||
// (instant-validation, async storage, dynamic rendering) into the
|
||||
// client render path. CI runs this suite both with and without it
|
||||
// (see test/cache-components-tests-manifest.json).
|
||||
const cacheComponents = process.env.__NEXT_CACHE_COMPONENTS === 'true'
|
||||
if (process.env.IS_TURBOPACK_TEST) {
|
||||
if (cacheComponents) {
|
||||
expect(serverSources).toMatchInlineSnapshot(`
|
||||
[
|
||||
"src/server/app-render/async-local-storage.ts",
|
||||
"src/server/app-render/instant-validation/boundary-constants.ts",
|
||||
"src/server/app-render/instant-validation/boundary-impl.tsx",
|
||||
"src/server/app-render/instant-validation/instant-samples-client.ts",
|
||||
"src/server/app-render/instant-validation/instant-samples.ts",
|
||||
"src/server/app-render/instant-validation/instant-validation-error.ts",
|
||||
"src/server/app-render/staged-rendering.ts",
|
||||
"src/server/app-render/work-async-storage-instance.ts",
|
||||
"src/server/app-render/work-async-storage.external.ts",
|
||||
"src/server/app-render/work-unit-async-storage-instance.ts",
|
||||
"src/server/app-render/work-unit-async-storage.external.ts",
|
||||
"src/server/web/spec-extension/adapters/headers.ts",
|
||||
"src/server/web/spec-extension/adapters/reflect.ts",
|
||||
"src/server/web/spec-extension/adapters/request-cookies.ts",
|
||||
"src/server/web/spec-extension/cookies.ts",
|
||||
]
|
||||
`)
|
||||
} else {
|
||||
expect(serverSources).toMatchInlineSnapshot(`[]`)
|
||||
}
|
||||
} else {
|
||||
if (cacheComponents) {
|
||||
expect(serverSources).toMatchInlineSnapshot(`[]`)
|
||||
} else {
|
||||
expect(serverSources).toMatchInlineSnapshot(`[]`)
|
||||
}
|
||||
}
|
||||
expect(serverSources).toEqual([])
|
||||
})
|
||||
|
||||
it('must not bundle any dev overlay into browser chunks', () => {
|
||||
|
||||
Reference in New Issue
Block a user