Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move redirect helpers into separate module #73118

Merged
merged 2 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,8 @@ import { useNavFailureHandler } from './nav-failure-handler'
import { useServerActionDispatcher } from '../app-call-server'
import type { AppRouterActionQueue } from '../../shared/lib/router/action-queue'
import { prefetch as prefetchWithSegmentCache } from '../components/segment-cache/prefetch'

import {
getRedirectTypeFromError,
getURLFromRedirectError,
isRedirectError,
RedirectType,
} from './redirect'
import { getRedirectTypeFromError, getURLFromRedirectError } from './redirect'
import { isRedirectError, RedirectType } from './redirect-error'

const globalMutable: {
pendingMpaPath?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
isHTTPAccessFallbackError,
type HTTPAccessFallbackError,
} from './http-access-fallback/http-access-fallback'
import { isRedirectError, type RedirectError } from './redirect'
import { isRedirectError, type RedirectError } from './redirect-error'

/**
* Returns true if the error is a navigation signal error. These errors are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class ReadonlyURLSearchParams extends URLSearchParams {
}
}

export { redirect, permanentRedirect, RedirectType } from './redirect'
export { redirect, permanentRedirect } from './redirect'
export { RedirectType } from './redirect-error'
export { notFound } from './not-found'
export { forbidden } from './forbidden'
export { unauthorized } from './unauthorized'
Expand Down
8 changes: 2 additions & 6 deletions packages/next/src/client/components/redirect-boundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
import React, { useEffect } from 'react'
import type { AppRouterInstance } from '../../shared/lib/app-router-context.shared-runtime'
import { useRouter } from './navigation'
import {
RedirectType,
getRedirectTypeFromError,
getURLFromRedirectError,
isRedirectError,
} from './redirect'
import { getRedirectTypeFromError, getURLFromRedirectError } from './redirect'
import { RedirectType, isRedirectError } from './redirect-error'

interface RedirectBoundaryProps {
router: AppRouterInstance
Expand Down
45 changes: 45 additions & 0 deletions packages/next/src/client/components/redirect-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { RedirectStatusCode } from './redirect-status-code'

export const REDIRECT_ERROR_CODE = 'NEXT_REDIRECT'

export enum RedirectType {
push = 'push',
replace = 'replace',
}

export type RedirectError = Error & {
digest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`
}

/**
* Checks an error to determine if it's an error generated by the
* `redirect(url)` helper.
*
* @param error the error that may reference a redirect error
* @returns true if the error is a redirect error
*/
export function isRedirectError(error: unknown): error is RedirectError {
if (
typeof error !== 'object' ||
error === null ||
!('digest' in error) ||
typeof error.digest !== 'string'
) {
return false
}

const digest = error.digest.split(';')
const [errorCode, type] = digest
const destination = digest.slice(2, -2).join(';')
const status = digest.at(-2)

const statusCode = Number(status)

return (
errorCode === REDIRECT_ERROR_CODE &&
(type === 'replace' || type === 'push') &&
typeof destination === 'string' &&
!isNaN(statusCode) &&
statusCode in RedirectStatusCode
)
}
4 changes: 3 additions & 1 deletion packages/next/src/client/components/redirect.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { getURLFromRedirectError, isRedirectError, redirect } from './redirect'
import { getURLFromRedirectError, redirect } from './redirect'
import { isRedirectError } from './redirect-error'

describe('test', () => {
it('should throw a redirect error', () => {
try {
Expand Down
50 changes: 6 additions & 44 deletions packages/next/src/client/components/redirect.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { actionAsyncStorage } from '../../server/app-render/action-async-storage.external'
import { RedirectStatusCode } from './redirect-status-code'

const REDIRECT_ERROR_CODE = 'NEXT_REDIRECT'

export enum RedirectType {
push = 'push',
replace = 'replace',
}

export type RedirectError = Error & {
digest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`
}
import {
RedirectType,
type RedirectError,
isRedirectError,
REDIRECT_ERROR_CODE,
} from './redirect-error'

export function getRedirectError(
url: string,
Expand Down Expand Up @@ -68,39 +63,6 @@ export function permanentRedirect(
throw getRedirectError(url, type, RedirectStatusCode.PermanentRedirect)
}

/**
* Checks an error to determine if it's an error generated by the
* `redirect(url)` helper.
*
* @param error the error that may reference a redirect error
* @returns true if the error is a redirect error
*/
export function isRedirectError(error: unknown): error is RedirectError {
if (
typeof error !== 'object' ||
error === null ||
!('digest' in error) ||
typeof error.digest !== 'string'
) {
return false
}

const digest = error.digest.split(';')
const [errorCode, type] = digest
const destination = digest.slice(2, -2).join(';')
const status = digest.at(-2)

const statusCode = Number(status)

return (
errorCode === REDIRECT_ERROR_CODE &&
(type === 'replace' || type === 'push') &&
typeof destination === 'string' &&
!isNaN(statusCode) &&
statusCode in RedirectStatusCode
)
}

/**
* Returns the encoded URL from the error if it's a RedirectError, null
* otherwise. Note that this does not validate the URL returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ import {
normalizeFlightData,
type NormalizedFlightData,
} from '../../../flight-data-helpers'
import { getRedirectError, RedirectType } from '../../redirect'
import { getRedirectError } from '../../redirect'
import { RedirectType } from '../../redirect-error'
import { createSeededPrefetchCacheEntry } from '../prefetch-cache-utils'
import { removeBasePath } from '../../../remove-base-path'
import { hasBasePath } from '../../../has-base-path'
Expand Down
4 changes: 3 additions & 1 deletion packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import {
import {
getRedirectTypeFromError,
getURLFromRedirectError,
} from '../../client/components/redirect'
import {
isRedirectError,
type RedirectType,
} from '../../client/components/redirect'
} from '../../client/components/redirect-error'
import RenderResult from '../render-result'
import type { WorkStore } from '../app-render/work-async-storage.external'
import { FlightRenderResult } from './flight-render-result'
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ import {
} from '../../client/components/http-access-fallback/http-access-fallback'
import {
getURLFromRedirectError,
isRedirectError,
getRedirectStatusCodeFromError,
} from '../../client/components/redirect'
import { isRedirectError } from '../../client/components/redirect-error'
import { getImplicitTags } from '../lib/implicit-tags'
import { AppRenderSpan, NextNodeServerSpan } from '../lib/trace/constants'
import { getTracer } from '../lib/trace/tracer'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { type JSX } from 'react'
import { isHTTPAccessFallbackError } from '../../client/components/http-access-fallback/http-access-fallback'
import {
getURLFromRedirectError,
isRedirectError,
getRedirectStatusCodeFromError,
} from '../../client/components/redirect'
import { isRedirectError } from '../../client/components/redirect-error'
import { renderToReadableStream } from 'react-dom/server.edge'
import { streamToString } from '../stream-utils/node-web-streams-helper'
import { RedirectStatusCode } from '../../client/components/redirect-status-code'
Expand Down
4 changes: 3 additions & 1 deletion packages/next/src/server/route-modules/app-route/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ import type { AppSegment } from '../../../build/segment-config/app/app-segments'
import {
getRedirectStatusCodeFromError,
getURLFromRedirectError,
} from '../../../client/components/redirect'
import {
isRedirectError,
type RedirectError,
} from '../../../client/components/redirect'
} from '../../../client/components/redirect-error'
import {
getAccessFallbackHTTPStatus,
isHTTPAccessFallbackError,
Expand Down
Loading