Skip to content

Commit

Permalink
fix: ensure that the urlPathname is always a pathname
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Jun 11, 2024
1 parent 6606630 commit 3fa4568
Show file tree
Hide file tree
Showing 35 changed files with 242 additions and 219 deletions.
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,12 @@ import { staticGenerationAsyncStorage } from './static-generation-async-storage-

export interface StaticGenerationStore {
readonly isStaticGeneration: boolean
readonly pagePath?: string
readonly urlPathname: string
/**
* The route that is being rendered. This is the path to the page file
* excluding the trailing `/page` or `/route`.
*/
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')
}

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}
page={staticGenerationStore.route}
/>,
loadingData,
],
Expand Down
Loading

0 comments on commit 3fa4568

Please sign in to comment.