diff --git a/packages/next/client/components/app-router.tsx b/packages/next/client/components/app-router.tsx index e0b2c964911f..a1ed740d1094 100644 --- a/packages/next/client/components/app-router.tsx +++ b/packages/next/client/components/app-router.tsx @@ -26,7 +26,7 @@ import { // ParamsContext, PathnameContext, // LayoutSegmentsContext, -} from './hooks-client-context' +} from '../../shared/lib/hooks-client-context' import { useReducerWithReduxDevtools } from './use-reducer-with-devtools' import { ErrorBoundary, GlobalErrorComponent } from './error-boundary' diff --git a/packages/next/client/components/navigation.ts b/packages/next/client/components/navigation.ts index f12b6d793ade..39b3d7092521 100644 --- a/packages/next/client/components/navigation.ts +++ b/packages/next/client/components/navigation.ts @@ -11,7 +11,7 @@ import { // ParamsContext, PathnameContext, // LayoutSegmentsContext, -} from './hooks-client-context' +} from '../../shared/lib/hooks-client-context' import { staticGenerationBailout } from './static-generation-bailout' const INTERNAL_URLSEARCHPARAMS_INSTANCE = Symbol( diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index f9ec72ae7ca3..c64007f847db 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -35,7 +35,15 @@ import { ImageConfigComplete } from '../shared/lib/image-config' import { removeBasePath } from './remove-base-path' import { hasBasePath } from './has-base-path' import { AppRouterContext } from '../shared/lib/app-router-context' -import { adaptForAppRouterInstance } from '../shared/lib/router/adapters' +import { + adaptForAppRouterInstance, + adaptForPathname, + adaptForSearchParams, +} from '../shared/lib/router/adapters' +import { + PathnameContext, + SearchParamsContext, +} from '../shared/lib/hooks-client-context' const ReactDOM = process.env.__NEXT_REACT_ROOT ? require('react-dom/client') @@ -309,17 +317,21 @@ function AppContainer({ } > - - - - {children} - - - + + + + + + {children} + + + + + ) diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 5b08c664f1f8..d0fce3954e70 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -82,8 +82,16 @@ import { ImageConfigContext } from '../shared/lib/image-config-context' import stripAnsi from 'next/dist/compiled/strip-ansi' import { shouldUseReactRoot } from './utils' import { stripInternalQueries } from './internal-utils' -import { adaptForAppRouterInstance } from '../shared/lib/router/adapters' +import { + adaptForAppRouterInstance, + adaptForPathname, + adaptForSearchParams, +} from '../shared/lib/router/adapters' import { AppRouterContext } from '../shared/lib/app-router-context' +import { + PathnameContext, + SearchParamsContext, +} from '../shared/lib/hooks-client-context' let tryGetPreviewData: typeof import('./api-utils/node').tryGetPreviewData let warn: typeof import('../build/output/log').warn @@ -645,32 +653,36 @@ export async function renderToHTML( const AppContainer = ({ children }: { children: JSX.Element }) => ( - - - { - head = state - }, - updateScripts: (scripts) => { - scriptLoader = scripts - }, - scripts: initialScripts, - mountedInstances: new Set(), - }} - > - reactLoadableModules.push(moduleName)} - > - - - {children} - - - - - - + + + + + { + head = state + }, + updateScripts: (scripts) => { + scriptLoader = scripts + }, + scripts: initialScripts, + mountedInstances: new Set(), + }} + > + reactLoadableModules.push(moduleName)} + > + + + {children} + + + + + + + + ) diff --git a/packages/next/client/components/hooks-client-context.ts b/packages/next/shared/lib/hooks-client-context.ts similarity index 86% rename from packages/next/client/components/hooks-client-context.ts rename to packages/next/shared/lib/hooks-client-context.ts index b34157d876df..83d14c1451a0 100644 --- a/packages/next/client/components/hooks-client-context.ts +++ b/packages/next/shared/lib/hooks-client-context.ts @@ -2,8 +2,8 @@ import { createContext } from 'react' -export const SearchParamsContext = createContext(null as any) -export const PathnameContext = createContext(null as any) +export const SearchParamsContext = createContext(null) +export const PathnameContext = createContext(null) export const ParamsContext = createContext(null as any) export const LayoutSegmentsContext = createContext(null as any) diff --git a/packages/next/shared/lib/router/adapters.ts b/packages/next/shared/lib/router/adapters.ts index 5fae3ac15299..facd64c2a7ef 100644 --- a/packages/next/shared/lib/router/adapters.ts +++ b/packages/next/shared/lib/router/adapters.ts @@ -1,30 +1,81 @@ +import type { ParsedUrlQuery } from 'node:querystring' import { AppRouterInstance } from '../app-router-context' import { NextRouter } from './router' /** * adaptForAppRouterInstance implements the AppRouterInstance with a NextRouter. + * + * @param router the NextRouter to adapt + * @returns an AppRouterInstance */ export function adaptForAppRouterInstance( - nextRouter: NextRouter + router: NextRouter ): AppRouterInstance { return { back(): void { - nextRouter.back() + router.back() }, forward(): void { - nextRouter.forward() + router.forward() }, refresh(): void { - nextRouter.reload() + router.reload() }, push(href: string): void { - void nextRouter.push(href) + void router.push(href) }, replace(href: string): void { - void nextRouter.replace(href) + void router.replace(href) }, prefetch(href: string): void { - void nextRouter.prefetch(href) + void router.prefetch(href) }, } } + +/** + * transforms the ParsedUrlQuery into a URLSearchParams. + * + * @param query the query to transform + * @returns URLSearchParams + */ +function transformQuery(query: ParsedUrlQuery): URLSearchParams { + const params = new URLSearchParams() + + for (const [name, value] of Object.entries(query)) { + if (Array.isArray(value)) { + for (const val of value) { + params.append(name, val) + } + } else if (typeof value !== 'undefined') { + params.append(name, value) + } + } + + return params +} + +/** + * adaptForSearchParams transforms the ParsedURLQuery into URLSearchParams. + * + * @param router the router that contains the query. + * @returns the search params in the URLSearchParams format + */ +export function adaptForSearchParams(router: NextRouter): URLSearchParams { + if (!router.isReady || !router.query) { + return new URLSearchParams() + } + + return transformQuery(router.query) +} + +/** + * adaptForPathname adapts the `asPath` parameter from the router to a pathname. + * + * @param asPath the asPath parameter to transform that comes from the router + * @returns pathname part of `asPath` + */ +export function adaptForPathname(asPath: string): string { + const url = new URL(asPath, 'http://f') + return url.pathname +}