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

Ensure urlPathname is always a pathname #63846

Merged
merged 1 commit into from
Jun 11, 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
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ pub async fn get_app_page_entry(
indexmap! {
"VAR_DEFINITION_PAGE" => page.to_string().into(),
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
// TODO(alexkirsz) Support custom global error.
"VAR_MODULE_GLOBAL_ERROR" => "next/dist/client/components/error-boundary".into(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ pub async fn get_app_route_entry(
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
"VAR_DEFINITION_FILENAME" => path.file_stem().await?.as_ref().unwrap().as_str().into(),
// TODO(alexkirsz) Is this necessary?
"VAR_DEFINITION_BUNDLE_PATH" => "".into(),
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
"VAR_DEFINITION_BUNDLE_PATH" => "".to_string().into(),
"VAR_RESOLVED_PAGE_PATH" => path.to_string().await?.clone_value(),
"VAR_USERLAND" => INNER.into(),
},
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/build/templates/app-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ declare const __next_app_load_chunk__: any
// INJECT:__next_app_require__
// INJECT:__next_app_load_chunk__

export const originalPathname = 'VAR_ORIGINAL_PATHNAME'
export const __next_app__ = {
require: __next_app_require__,
loadChunk: __next_app_load_chunk__,
Expand Down
5 changes: 1 addition & 4 deletions packages/next/src/build/templates/app-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,14 @@ const routeModule = new AppRouteRouteModule({
const { requestAsyncStorage, staticGenerationAsyncStorage, serverHooks } =
routeModule

const originalPathname = 'VAR_ORIGINAL_PATHNAME'

function patchFetch() {
return _patchFetch({ staticGenerationAsyncStorage })
return _patchFetch({ staticGenerationAsyncStorage, requestAsyncStorage })
}

export {
routeModule,
requestAsyncStorage,
staticGenerationAsyncStorage,
serverHooks,
originalPathname,
patchFetch,
}
3 changes: 1 addition & 2 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1384,9 +1384,8 @@ export async function buildAppStaticPaths({
return StaticGenerationAsyncStorageWrapper.wrap(
ComponentMod.staticGenerationAsyncStorage,
{
urlPathname: page,
page,
renderOpts: {
originalPathname: page,
incrementalCache,
supportsDynamicResponse: true,
isRevalidate: false,
Expand Down
2 changes: 0 additions & 2 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ async function createAppRouteCode({
VAR_DEFINITION_FILENAME: fileBaseName,
VAR_DEFINITION_BUNDLE_PATH: bundlePath,
VAR_RESOLVED_PAGE_PATH: resolvedPagePath,
VAR_ORIGINAL_PATHNAME: page,
},
{
nextConfigOutput: JSON.stringify(nextConfigOutput),
Expand Down Expand Up @@ -772,7 +771,6 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
VAR_DEFINITION_PAGE: page,
VAR_DEFINITION_PATHNAME: pathname,
VAR_MODULE_GLOBAL_ERROR: treeCodeResult.globalError,
VAR_ORIGINAL_PATHNAME: page,
},
{
tree: treeCodeResult.treeCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ import type { DeepReadonly } from '../../shared/lib/deep-readonly'
import type { AfterContext } from '../../server/after/after-context'

export interface RequestStore {
/**
* The URL of the request. This only specifies the pathname and the search
* part of the URL.
*/
readonly url: {
/**
* The pathname of the requested URL.
*/
readonly pathname: string

/**
* The search part of the requested URL. If the request did not provide a
* search part, this will be an empty string.
*/
readonly search: string
}

readonly headers: ReadonlyHeaders
readonly cookies: ReadonlyRequestCookies
readonly mutableCookies: ResponseCookies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,18 @@ import { staticGenerationAsyncStorage } from './static-generation-async-storage-

export interface StaticGenerationStore {
readonly isStaticGeneration: boolean
readonly pagePath?: string
readonly urlPathname: string

/**
* The page that is being rendered. This relates to the path to the page file.
*/
readonly page: string

/**
* The route that is being rendered. This is the page property without the
* trailing `/page` or `/route` suffix.
*/
readonly route: string

readonly incrementalCache?: IncrementalCache
readonly isOnDemandRevalidate?: boolean
readonly isPrerendering?: boolean
Expand Down
3 changes: 1 addition & 2 deletions packages/next/src/export/routes/app-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ export async function exportAppRoute(
notFoundRoutes: [],
},
renderOpts: {
experimental: experimental,
originalPathname: page,
experimental,
nextExport: true,
supportsDynamicResponse: false,
incrementalCache,
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ async function exportPageImpl(
fontManifest: optimizeFonts ? requireFontManifest(distDir) : undefined,
locale,
supportsDynamicResponse: false,
originalPathname: page,
experimental: {
...input.renderOpts.experimental,
isRoutePPREnabled,
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/lib/metadata/metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ import { isNotFoundError } from '../../client/components/not-found'
import type { MetadataContext } from './types/resolvers'

export function createMetadataContext(
urlPathname: string,
pathname: string,
renderOpts: AppRenderContext['renderOpts']
): MetadataContext {
return {
pathname: urlPathname.split('?')[0],
pathname,
trailingSlash: renderOpts.trailingSlash,
isStandaloneMode: renderOpts.nextConfigOutput === 'standalone',
}
Expand Down
10 changes: 1 addition & 9 deletions packages/next/src/lib/url.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers'

export const DUMMY_ORIGIN = 'http://n'

function getUrlWithoutHost(url: string) {
return new URL(url, DUMMY_ORIGIN)
}

export function getPathname(url: string) {
return getUrlWithoutHost(url).pathname
}
const DUMMY_ORIGIN = 'http://n'

export function isFullStringUrl(url: string) {
return /https?:\/\//.test(url)
Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/server/after/after-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,13 +463,14 @@ describe('createAfterContext', () => {

const createMockRequestStore = (afterContext: AfterContext): RequestStore => {
const partialStore: Partial<RequestStore> = {
url: { pathname: '/', search: '' },
afterContext: afterContext,
assetPrefix: '',
reactLoadableManifest: {},
draftMode: undefined,
}

return new Proxy(partialStore, {
return new Proxy(partialStore as RequestStore, {
get(target, key) {
if (key in target) {
return target[key as keyof typeof target]
Expand All @@ -478,5 +479,5 @@ const createMockRequestStore = (afterContext: AfterContext): RequestStore => {
`RequestStore property not mocked: '${typeof key === 'symbol' ? key.toString() : key}'`
)
},
}) as RequestStore
})
}
1 change: 1 addition & 0 deletions packages/next/src/server/after/after-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ function wrapRequestStoreForAfterCallbacks(
requestStore: RequestStore
): RequestStore {
return {
url: requestStore.url,
get headers() {
return requestStore.headers
},
Expand Down
4 changes: 1 addition & 3 deletions packages/next/src/server/after/after.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { getExpectedRequestStore } from '../../client/components/request-async-storage.external'
import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external'
import { StaticGenBailoutError } from '../../client/components/static-generation-bailout'
import { getPathname } from '../../lib/url'

import { markCurrentScopeAsDynamic } from '../app-render/dynamic-rendering'

Expand All @@ -27,9 +26,8 @@ export function unstable_after<T>(task: AfterTask<T>) {

if (staticGenerationStore) {
if (staticGenerationStore.forceStatic) {
const pathname = getPathname(staticGenerationStore.urlPathname)
throw new StaticGenBailoutError(
`Route ${pathname} with \`dynamic = "force-static"\` couldn't be rendered statically because it used \`${callingExpression}\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`
`Route ${staticGenerationStore.route} with \`dynamic = "force-static"\` couldn't be rendered statically because it used \`${callingExpression}\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`
)
} else {
markCurrentScopeAsDynamic(staticGenerationStore, callingExpression)
Expand Down
31 changes: 17 additions & 14 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ import {
import { getSegmentParam } from './get-segment-param'
import { getScriptNonceFromHeader } from './get-script-nonce-from-header'
import { parseAndValidateFlightRouterState } from './parse-and-validate-flight-router-state'
import { validateURL } from './validate-url'
import { createFlightRouterStateFromLoaderTree } from './create-flight-router-state-from-loader-tree'
import { handleAction } from './action-handler'
import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr'
Expand Down Expand Up @@ -116,6 +115,7 @@ import {
import { createServerModuleMap } from './action-utils'
import { isNodeNextRequest } from '../base-http/helpers'
import { parseParameter } from '../../shared/lib/router/utils/route-regex'
import { parseRelativeUrl } from '../../shared/lib/router/utils/parse-relative-url'

export type GetDynamicParamFromSegment = (
// [slug] / [[slug]] / [...slug]
Expand Down Expand Up @@ -319,7 +319,7 @@ async function generateFlight(
},
getDynamicParamFromSegment,
appUsingSizeAdjustment,
staticGenerationStore: { urlPathname },
requestStore: { url },
query,
requestId,
flightRouterState,
Expand All @@ -329,7 +329,7 @@ async function generateFlight(
const [MetadataTree, MetadataOutlet] = createMetadataComponents({
tree: loaderTree,
query,
metadataContext: createMetadataContext(urlPathname, ctx.renderOpts),
metadataContext: createMetadataContext(url.pathname, ctx.renderOpts),
getDynamicParamFromSegment,
appUsingSizeAdjustment,
createDynamicallyTrackedSearchParams,
Expand Down Expand Up @@ -441,7 +441,7 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
GlobalError,
createDynamicallyTrackedSearchParams,
},
staticGenerationStore: { urlPathname },
requestStore: { url },
} = ctx
const initialTree = createFlightRouterStateFromLoaderTree(
tree,
Expand All @@ -453,7 +453,7 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
tree,
errorType: asNotFound ? 'not-found' : undefined,
query,
metadataContext: createMetadataContext(urlPathname, ctx.renderOpts),
metadataContext: createMetadataContext(url.pathname, ctx.renderOpts),
getDynamicParamFromSegment: getDynamicParamFromSegment,
appUsingSizeAdjustment: appUsingSizeAdjustment,
createDynamicallyTrackedSearchParams,
Expand Down Expand Up @@ -485,7 +485,7 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
<AppRouter
buildId={ctx.renderOpts.buildId}
assetPrefix={ctx.assetPrefix}
initialCanonicalUrl={urlPathname}
initialCanonicalUrl={url.pathname + url.search}
// This is the router state tree.
initialTree={initialTree}
// This is the tree of React nodes that are seeded into the cache
Expand Down Expand Up @@ -530,14 +530,14 @@ async function ReactServerError({
GlobalError,
createDynamicallyTrackedSearchParams,
},
staticGenerationStore: { urlPathname },
requestStore: { url },
requestId,
res,
} = ctx

const [MetadataTree] = createMetadataComponents({
tree,
metadataContext: createMetadataContext(urlPathname, ctx.renderOpts),
metadataContext: createMetadataContext(url.pathname, ctx.renderOpts),
errorType,
query,
getDynamicParamFromSegment,
Expand Down Expand Up @@ -579,7 +579,7 @@ async function ReactServerError({
<AppRouter
buildId={ctx.renderOpts.buildId}
assetPrefix={ctx.assetPrefix}
initialCanonicalUrl={urlPathname}
initialCanonicalUrl={url.pathname + url.search}
initialTree={initialTree}
initialHead={head}
initialLayerAssets={null}
Expand Down Expand Up @@ -1407,7 +1407,7 @@ async function renderToHTMLOrFlightImpl(
])
}

addImplicitTags(staticGenerationStore)
addImplicitTags(staticGenerationStore, requestStore)

if (staticGenerationStore.tags) {
metadata.fetchTags = staticGenerationStore.tags.join(',')
Expand Down Expand Up @@ -1498,17 +1498,20 @@ export const renderToHTMLOrFlight: AppPageRender = (
query,
renderOpts
) => {
// TODO: this includes query string, should it?
const pathname = validateURL(req.url)
if (!req.url) {
throw new Error('Invalid URL')
huozhi marked this conversation as resolved.
Show resolved Hide resolved
}

const url = parseRelativeUrl(req.url, undefined, false)

return RequestAsyncStorageWrapper.wrap(
renderOpts.ComponentMod.requestAsyncStorage,
{ req, res, renderOpts },
{ req, url, res, renderOpts },
(requestStore) =>
StaticGenerationAsyncStorageWrapper.wrap(
renderOpts.ComponentMod.staticGenerationAsyncStorage,
{
urlPathname: pathname,
page: renderOpts.routeModule.definition.page,
renderOpts,
requestEndedState: { ended: false },
},
Expand Down
7 changes: 2 additions & 5 deletions packages/next/src/server/app-render/create-component-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,7 @@ async function createComponentTreeInternal({
}

if (typeof layoutOrPageMod?.revalidate !== 'undefined') {
validateRevalidate(
layoutOrPageMod?.revalidate,
staticGenerationStore.urlPathname
)
validateRevalidate(layoutOrPageMod?.revalidate, staticGenerationStore.route)
}

if (typeof layoutOrPageMod?.revalidate === 'number') {
Expand Down Expand Up @@ -537,7 +534,7 @@ async function createComponentTreeInternal({
<Postpone
prerenderState={staticGenerationStore.prerenderState}
reason='dynamic = "force-dynamic" was used'
pathname={staticGenerationStore.urlPathname}
route={staticGenerationStore.route}
/>,
loadingData,
],
Expand Down
Loading
Loading