Skip to content

Commit

Permalink
Merge branch 'canary' into issues/54008-Nginx-decoded-URIs-breaks-slu…
Browse files Browse the repository at this point in the history
…gs-client-side
  • Loading branch information
huozhi authored Oct 2, 2023
2 parents 122843c + 86274e6 commit 49c9869
Show file tree
Hide file tree
Showing 41 changed files with 606 additions and 53 deletions.
2 changes: 1 addition & 1 deletion examples/with-stripe-typescript/app/api/webhooks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function POST(req: Request) {
console.log(`💰 PaymentIntent status: ${data.status}`)
break
default:
throw new Error(`Unhhandled event: ${event.type}`)
throw new Error(`Unhandled event: ${event.type}`)
}
} catch (error) {
console.log(error)
Expand Down
21 changes: 21 additions & 0 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ import { eventSwcPlugins } from '../telemetry/events/swc-plugins'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import {
ACTION,
NEXT_ROUTER_PREFETCH,
RSC,
RSC_CONTENT_TYPE_HEADER,
RSC_VARY_HEADER,
Expand Down Expand Up @@ -227,6 +228,7 @@ export type RoutesManifest = {
rsc: {
header: typeof RSC
varyHeader: typeof RSC_VARY_HEADER
prefetchHeader: typeof NEXT_ROUTER_PREFETCH
}
skipMiddlewareUrlNormalize?: boolean
caseSensitive?: boolean
Expand Down Expand Up @@ -795,6 +797,7 @@ export default async function build(
rsc: {
header: RSC,
varyHeader: RSC_VARY_HEADER,
prefetchHeader: NEXT_ROUTER_PREFETCH,
contentTypeHeader: RSC_CONTENT_TYPE_HEADER,
},
skipMiddlewareUrlNormalize: config.skipMiddlewareUrlNormalize,
Expand Down Expand Up @@ -1055,6 +1058,7 @@ export default async function build(
const additionalSsgPaths = new Map<string, Array<string>>()
const additionalSsgPathsEncoded = new Map<string, Array<string>>()
const appStaticPaths = new Map<string, Array<string>>()
const appPrefetchPaths = new Map<string, string>()
const appStaticPathsEncoded = new Map<string, Array<string>>()
const appNormalizedPaths = new Map<string, string>()
const appDynamicParamPaths = new Set<string>()
Expand Down Expand Up @@ -1554,6 +1558,14 @@ export default async function build(
appDynamicParamPaths.add(originalAppPath)
}
appDefaultConfigs.set(originalAppPath, appConfig)

if (
!isStatic &&
!isAppRouteRoute(originalAppPath) &&
!isDynamicRoute(originalAppPath)
) {
appPrefetchPaths.set(originalAppPath, page)
}
}
} else {
if (isEdgeRuntime(pageRuntime)) {
Expand Down Expand Up @@ -2001,6 +2013,15 @@ export default async function build(
})
})

for (const [originalAppPath, page] of appPrefetchPaths) {
defaultMap[page] = {
page: originalAppPath,
query: {},
_isAppDir: true,
_isAppPrefetch: true,
}
}

if (i18n) {
for (const page of [
...staticPages,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function createRouterCacheKey(
withoutSearchParameters: boolean = false
) {
return Array.isArray(segment)
? `${segment[0]}|${segment[1]}|${segment[2]}`
? `${segment[0]}|${segment[1]}|${segment[2]}`.toLowerCase()
: withoutSearchParameters && segment.startsWith('__PAGE__')
? '__PAGE__'
: segment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface StaticGenerationStore {
dynamicUsageDescription?: string
dynamicUsageStack?: string
dynamicUsageErr?: DynamicServerError
staticPrefetchBailout?: boolean

nextFetchId?: number
pathWasRevalidated?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export const staticGenerationBailout: StaticGenerationBailout = (

if (staticGenerationStore) {
staticGenerationStore.revalidate = 0

if (!opts?.dynamic) {
// we can statically prefetch pages that opt into dynamic,
// but not things like headers/cookies
staticGenerationStore.staticPrefetchBailout = true
}
}

if (staticGenerationStore?.isStaticGeneration) {
Expand Down
63 changes: 62 additions & 1 deletion packages/next/src/export/routes/app-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import type { NextParsedUrlQuery } from '../../server/request-meta'

import fs from 'fs/promises'
import { MockedRequest, MockedResponse } from '../../server/lib/mock-request'
import {
RSC,
NEXT_URL,
NEXT_ROUTER_PREFETCH,
} from '../../client/components/app-router-headers'
import { isDynamicUsageError } from '../helpers/is-dynamic-usage-error'
import { NEXT_CACHE_TAGS_HEADER } from '../../lib/constants'
import { hasNextSupport } from '../../telemetry/ci-info'
Expand All @@ -19,6 +24,37 @@ const render: AppPageRender = (...args) => {
)
}

export async function generatePrefetchRsc(
req: MockedRequest,
path: string,
res: MockedResponse,
pathname: string,
htmlFilepath: string,
renderOpts: RenderOpts
) {
req.headers[RSC.toLowerCase()] = '1'
req.headers[NEXT_URL.toLowerCase()] = path
req.headers[NEXT_ROUTER_PREFETCH.toLowerCase()] = '1'

renderOpts.supportsDynamicHTML = true
renderOpts.isPrefetch = true
delete renderOpts.isRevalidate

const prefetchRenderResult = await render(req, res, pathname, {}, renderOpts)

prefetchRenderResult.pipe(res)
await res.hasStreamed

const prefetchRscData = Buffer.concat(res.buffers)

if ((renderOpts as any).store.staticPrefetchBailout) return

await fs.writeFile(
htmlFilepath.replace(/\.html$/, '.prefetch.rsc'),
prefetchRscData
)
}

export async function exportAppPage(
req: MockedRequest,
res: MockedResponse,
Expand All @@ -29,14 +65,28 @@ export async function exportAppPage(
renderOpts: RenderOpts,
htmlFilepath: string,
debugOutput: boolean,
isDynamicError: boolean
isDynamicError: boolean,
isAppPrefetch: boolean
): Promise<ExportPageResult> {
// If the page is `/_not-found`, then we should update the page to be `/404`.
if (page === '/_not-found') {
pathname = '/404'
}

try {
if (isAppPrefetch) {
await generatePrefetchRsc(
req,
path,
res,
pathname,
htmlFilepath,
renderOpts
)

return { fromBuildExportRevalidate: 0 }
}

const result = await render(req, res, pathname, query, renderOpts)
const html = result.toUnchunkedString()
const { metadata } = result
Expand All @@ -50,6 +100,17 @@ export async function exportAppPage(
)
}

if (!(renderOpts as any).store.staticPrefetchBailout) {
await generatePrefetchRsc(
req,
path,
res,
pathname,
htmlFilepath,
renderOpts
)
}

const { staticBailoutInfo = {} } = metadata

if (revalidate === 0 && debugOutput && staticBailoutInfo?.description) {
Expand Down
6 changes: 5 additions & 1 deletion packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ async function exportPageImpl(input: ExportPageInput) {
// Check if this is an `app/` page.
_isAppDir: isAppDir = false,

// Check if this is an `app/` prefix request.
_isAppPrefetch: isAppPrefetch = false,

// Check if this should error when dynamic usage is detected.
_isDynamicError: isDynamicError = false,

Expand Down Expand Up @@ -306,7 +309,8 @@ async function exportPageImpl(input: ExportPageInput) {
renderOpts,
htmlFilepath,
debugOutput,
isDynamicError
isDynamicError,
isAppPrefetch
)
}

Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/lib/turbopack-warning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ const supportedTurbopackNextConfigOptions = [
'experimental.deploymentId',

// Experimental options that don't affect compilation
'serverRuntimeConfig',
'publicRuntimeConfig',
'experimental.proxyTimeout',
'experimental.caseSensitiveRoutes',
'experimental.workerThreads',
Expand Down
59 changes: 31 additions & 28 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ export const renderToHTMLOrFlight: AppPageRender = (
href={fullHref}
// @ts-ignore
precedence={precedence}
crossOrigin={renderOpts.crossOrigin}
key={index}
/>
)
Expand Down Expand Up @@ -511,7 +512,7 @@ export const renderToHTMLOrFlight: AppPageRender = (
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFilename)![1]
const type = `font/${ext}`
const href = `${assetPrefix}/_next/${fontFilename}`
ComponentMod.preloadFont(href, type)
ComponentMod.preloadFont(href, type, renderOpts.crossOrigin)
}
} else {
try {
Expand Down Expand Up @@ -546,14 +547,15 @@ export const renderToHTMLOrFlight: AppPageRender = (
const precedence =
process.env.NODE_ENV === 'development' ? 'next_' + href : 'next'

ComponentMod.preloadStyle(fullHref)
ComponentMod.preloadStyle(fullHref, renderOpts.crossOrigin)

return (
<link
rel="stylesheet"
href={fullHref}
// @ts-ignore
precedence={precedence}
crossOrigin={renderOpts.crossOrigin}
key={index}
/>
)
Expand Down Expand Up @@ -1106,6 +1108,13 @@ export const renderToHTMLOrFlight: AppPageRender = (
// Explicit refresh
flightRouterState[3] === 'refetch'

const shouldSkipComponentTree =
isPrefetch &&
!Boolean(components.loading) &&
(flightRouterState ||
// If there is no flightRouterState, we need to check the entire loader tree, as otherwise we'll be only checking the root
!hasLoadingComponentInTree(loaderTree))

if (!parentRendered && renderComponentsOnThisLevel) {
const overriddenSegment =
flightRouterState &&
Expand All @@ -1122,9 +1131,7 @@ export const renderToHTMLOrFlight: AppPageRender = (
getDynamicParamFromSegment,
query
),
isPrefetch &&
!Boolean(components.loading) &&
!hasLoadingComponentInTree(loaderTree)
shouldSkipComponentTree
? null
: // Create component tree using the slice of the loaderTree
// @ts-expect-error TODO-APP: fix async component type
Expand All @@ -1147,9 +1154,7 @@ export const renderToHTMLOrFlight: AppPageRender = (

return <Component />
}),
isPrefetch &&
!Boolean(components.loading) &&
!hasLoadingComponentInTree(loaderTree)
shouldSkipComponentTree
? null
: (() => {
const { layoutOrPagePath } =
Expand Down Expand Up @@ -1446,21 +1451,26 @@ export const renderToHTMLOrFlight: AppPageRender = (
tree: LoaderTree
formState: any
}) => {
const polyfills = buildManifest.polyfillFiles
.filter(
(polyfill) =>
polyfill.endsWith('.js') && !polyfill.endsWith('.module.js')
)
.map((polyfill) => ({
src: `${assetPrefix}/_next/${polyfill}${getAssetQueryString(
false
)}`,
integrity: subresourceIntegrityManifest?.[polyfill],
}))
const polyfills: JSX.IntrinsicElements['script'][] =
buildManifest.polyfillFiles
.filter(
(polyfill) =>
polyfill.endsWith('.js') && !polyfill.endsWith('.module.js')
)
.map((polyfill) => ({
src: `${assetPrefix}/_next/${polyfill}${getAssetQueryString(
false
)}`,
integrity: subresourceIntegrityManifest?.[polyfill],
crossOrigin: renderOpts.crossOrigin,
noModule: true,
nonce,
}))

const [preinitScripts, bootstrapScript] = getRequiredScripts(
buildManifest,
assetPrefix,
renderOpts.crossOrigin,
subresourceIntegrityManifest,
getAssetQueryString(true),
nonce
Expand Down Expand Up @@ -1530,15 +1540,7 @@ export const renderToHTMLOrFlight: AppPageRender = (
{polyfillsFlushed
? null
: polyfills?.map((polyfill) => {
return (
<script
key={polyfill.src}
src={polyfill.src}
integrity={polyfill.integrity}
noModule={true}
nonce={nonce}
/>
)
return <script key={polyfill.src} {...polyfill} />
})}
{renderServerInsertedHTML()}
{errorMetaTags}
Expand Down Expand Up @@ -1648,6 +1650,7 @@ export const renderToHTMLOrFlight: AppPageRender = (
getRequiredScripts(
buildManifest,
assetPrefix,
renderOpts.crossOrigin,
subresourceIntegrityManifest,
getAssetQueryString(false),
nonce
Expand Down
Loading

0 comments on commit 49c9869

Please sign in to comment.