diff --git a/packages/next/client/components/app-router-headers.ts b/packages/next/client/components/app-router-headers.ts index aef43864e0a7..1eb2747dd1cd 100644 --- a/packages/next/client/components/app-router-headers.ts +++ b/packages/next/client/components/app-router-headers.ts @@ -1,36 +1,5 @@ -import { - DynamicParamTypesShort, - FlightRouterState, -} from '../../server/app-render' - export const RSC = 'RSC' as const export const NEXT_ROUTER_STATE_TREE = 'Next-Router-State-Tree' as const export const NEXT_ROUTER_PREFETCH = 'Next-Router-Prefetch' as const export const RSC_VARY_HEADER = `${RSC}, ${NEXT_ROUTER_STATE_TREE}, ${NEXT_ROUTER_PREFETCH}` as const - -export const escapeFlightRouterState = ( - state: FlightRouterState -): FlightRouterState => { - const [segment, parallelRoutes, ...restState] = state - const escapedParallelRoutes: typeof parallelRoutes = Object.create({}) - let escapedSegment: typeof segment = segment - - if (typeof segment !== 'string') { - const [param, value, type] = segment - const escapedSegmentValue = encodeURIComponent(value) - escapedSegment = [ - param, - escapedSegmentValue, - type as DynamicParamTypesShort, - ] - } - - Object.keys(parallelRoutes).forEach((key) => { - const childState = parallelRoutes[key] - - escapedParallelRoutes[key] = escapeFlightRouterState(childState) - }) - - return [escapedSegment, escapedParallelRoutes, ...restState] -} diff --git a/packages/next/client/components/app-router.tsx b/packages/next/client/components/app-router.tsx index 5788d81e507f..d29f680d5051 100644 --- a/packages/next/client/components/app-router.tsx +++ b/packages/next/client/components/app-router.tsx @@ -1,18 +1,18 @@ 'use client' import type { ReactNode } from 'react' -import React, { useCallback, useEffect, useMemo } from 'react' +import React, { useEffect, useMemo, useCallback } from 'react' import { createFromFetch } from 'next/dist/compiled/react-server-dom-webpack/client' -import type { - AppRouterInstance, - CacheNode, -} from '../../shared/lib/app-router-context' import { AppRouterContext, - GlobalLayoutRouterContext, LayoutRouterContext, + GlobalLayoutRouterContext, } from '../../shared/lib/app-router-context' -import type { FlightData, FlightRouterState } from '../../server/app-render' +import type { + CacheNode, + AppRouterInstance, +} from '../../shared/lib/app-router-context' +import type { FlightRouterState, FlightData } from '../../server/app-render' import { ACTION_NAVIGATE, ACTION_PREFETCH, @@ -22,13 +22,14 @@ import { reducer, } from './reducer' import { - PathnameContext, SearchParamsContext, + // ParamsContext, + PathnameContext, + // LayoutSegmentsContext, } from '../../shared/lib/hooks-client-context' import { useReducerWithReduxDevtools } from './use-reducer-with-devtools' import { ErrorBoundary, GlobalErrorComponent } from './error-boundary' import { - escapeFlightRouterState, NEXT_ROUTER_PREFETCH, NEXT_ROUTER_STATE_TREE, RSC, @@ -64,9 +65,7 @@ export async function fetchServerResponse( // Enable flight response [RSC]: '1', // Provide the current router state - [NEXT_ROUTER_STATE_TREE]: JSON.stringify( - escapeFlightRouterState(flightRouterState) - ), + [NEXT_ROUTER_STATE_TREE]: JSON.stringify(flightRouterState), } if (prefetch) { // Enable prefetch response diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index d5fd2404538d..8f122e0790cd 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -855,7 +855,13 @@ export async function renderToHTMLOrFlight( } const key = segmentParam.param - const value = pathParams[key] + let value = pathParams[key] + + if (Array.isArray(value)) { + value = value.map((i) => encodeURIComponent(i)) + } else if (typeof value === 'string') { + value = encodeURIComponent(value) + } if (!value) { // Handle case where optional catchall does not have a value, e.g. `/dashboard/[...slug]` when requesting `/dashboard` diff --git a/test/unit/app-router-headers.test.ts b/test/unit/app-router-headers.test.ts deleted file mode 100644 index cfd2e3e3579e..000000000000 --- a/test/unit/app-router-headers.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { escapeFlightRouterState } from 'next/client/components/app-router-headers' -import { FlightRouterState } from 'next/server/app-render' - -const state1: FlightRouterState = [ - '', - { - children: [ - ['slug', 'свитер-urbain', 'd'], - { - children: ['', {}], - }, - ], - }, - null, - 'refetch', -] -const state1Escaped: FlightRouterState = [ - '', - { - children: [ - ['slug', '%D1%81%D0%B2%D0%B8%D1%82%D0%B5%D1%80-urbain', 'd'], - { - children: ['', {}], - }, - ], - }, - null, - 'refetch', -] - -const state2: FlightRouterState = [ - '', - { - children: [ - ['slug', 'свитер-urbain', 'd'], - { - children: ['', {}], - }, - ], - }, - null, - null, - true, -] -const state2Escaped: FlightRouterState = [ - '', - { - children: [ - ['slug', '%D1%81%D0%B2%D0%B8%D1%82%D0%B5%D1%80-urbain', 'd'], - { - children: ['', {}], - }, - ], - }, - null, - null, - true, -] - -const state3: FlightRouterState = [ - '', - { - children: [ - ['slug', 'свитер-urbain', 'd'], - { - children: ['', {}], - }, - null, - 'refetch', - ], - }, -] -const state3Escaped: FlightRouterState = [ - '', - { - children: [ - ['slug', '%D1%81%D0%B2%D0%B8%D1%82%D0%B5%D1%80-urbain', 'd'], - { - children: ['', {}], - }, - null, - 'refetch', - ], - }, -] - -describe('escapeFlightRouterState', () => { - it.each([ - { state: state1, escapedState: state1Escaped }, - { state: state2, escapedState: state2Escaped }, - { state: state3, escapedState: state3Escaped }, - ])('should escape non-iso segment value', ({ state, escapedState }) => { - expect(escapeFlightRouterState(state)).toMatchObject(escapedState) - }) -})