Skip to content

Commit

Permalink
fix: freeze loaded manifests
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Apr 11, 2024
1 parent 6bc9f79 commit d90fc32
Show file tree
Hide file tree
Showing 26 changed files with 327 additions and 84 deletions.
28 changes: 15 additions & 13 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ import { buildCustomRoute } from '../lib/build-custom-route'
import { createProgress } from './progress'
import { traceMemoryUsage } from '../lib/memory/trace'
import { generateEncryptionKeyBase64 } from '../server/app-render/encryption-utils'
import type { DeepReadonly } from '../shared/lib/deep-readonly'

interface ExperimentalBypassForInfo {
experimentalBypassFor?: RouteHas[]
Expand Down Expand Up @@ -337,27 +338,28 @@ async function readManifest<T extends object>(filePath: string): Promise<T> {

async function writePrerenderManifest(
distDir: string,
manifest: Readonly<PrerenderManifest>
manifest: DeepReadonly<PrerenderManifest>
): Promise<void> {
await writeManifest(path.join(distDir, PRERENDER_MANIFEST), manifest)
await writeEdgePartialPrerenderManifest(distDir, manifest)
}

async function writeEdgePartialPrerenderManifest(
distDir: string,
manifest: Readonly<Partial<PrerenderManifest>>
manifest: DeepReadonly<Partial<PrerenderManifest>>
): Promise<void> {
// We need to write a partial prerender manifest to make preview mode settings available in edge middleware.
// Use env vars in JS bundle and inject the actual vars to edge manifest.
const edgePartialPrerenderManifest: Partial<PrerenderManifest> = {
...manifest,
preview: {
previewModeId: 'process.env.__NEXT_PREVIEW_MODE_ID',
previewModeSigningKey: 'process.env.__NEXT_PREVIEW_MODE_SIGNING_KEY',
previewModeEncryptionKey:
'process.env.__NEXT_PREVIEW_MODE_ENCRYPTION_KEY',
},
}
const edgePartialPrerenderManifest: DeepReadonly<Partial<PrerenderManifest>> =
{
...manifest,
preview: {
previewModeId: 'process.env.__NEXT_PREVIEW_MODE_ID',
previewModeSigningKey: 'process.env.__NEXT_PREVIEW_MODE_SIGNING_KEY',
previewModeEncryptionKey:
'process.env.__NEXT_PREVIEW_MODE_ENCRYPTION_KEY',
},
}
await writeFileUtf8(
path.join(distDir, PRERENDER_MANIFEST.replace(/\.json$/, '.js')),
`self.__PRERENDER_MANIFEST=${JSON.stringify(
Expand All @@ -367,7 +369,7 @@ async function writeEdgePartialPrerenderManifest(
}

async function writeClientSsgManifest(
prerenderManifest: PrerenderManifest,
prerenderManifest: DeepReadonly<PrerenderManifest>,
{
buildId,
distDir,
Expand Down Expand Up @@ -3318,7 +3320,7 @@ export default async function build(
NextBuildContext.allowedRevalidateHeaderKeys =
config.experimental.allowedRevalidateHeaderKeys

const prerenderManifest: Readonly<PrerenderManifest> = {
const prerenderManifest: DeepReadonly<PrerenderManifest> = {
version: 4,
routes: finalPrerenderRoutes,
dynamicRoutes: finalDynamicRoutes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { SizeLimit } from '../../../../../types'
import { internal_getCurrentFunctionWaitUntil } from '../../../../server/web/internal-edge-wait-until'
import type { PAGE_TYPES } from '../../../../lib/page-types'
import type { NextRequestHint } from '../../../../server/web/adapter'
import type { DeepReadonly } from '../../../../shared/lib/deep-readonly'

export function getRender({
dev,
Expand Down Expand Up @@ -53,7 +54,7 @@ export function getRender({
renderToHTML?: any
Document: DocumentType
buildManifest: BuildManifest
prerenderManifest: PrerenderManifest
prerenderManifest: DeepReadonly<PrerenderManifest>
reactLoadableManifest: ReactLoadableManifest
subresourceIntegrityManifest?: Record<string, string>
interceptionRouteRewrites?: ManifestRewriteRoute[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export interface ManifestNode {
}

export type ClientReferenceManifest = {
moduleLoading: {
readonly moduleLoading: {
prefix: string
crossOrigin: string | null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import type { ReadonlyHeaders } from '../../server/web/spec-extension/adapters/h
import type { ReadonlyRequestCookies } from '../../server/web/spec-extension/adapters/request-cookies'

import { createAsyncLocalStorage } from './async-local-storage'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

export interface RequestStore {
readonly headers: ReadonlyHeaders
readonly cookies: ReadonlyRequestCookies
readonly mutableCookies: ResponseCookies
readonly draftMode: DraftModeProvider
readonly reactLoadableManifest: Record<string, { files: string[] }>
readonly reactLoadableManifest: DeepReadonly<
Record<string, { files: string[] }>
>
readonly assetPrefix: string
}

Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { formatManifest } from '../build/manifests/formatter/format-manifest'
import { validateRevalidate } from '../server/lib/patch-fetch'
import { TurborepoAccessTraceResult } from '../build/turborepo-access-trace'
import { createProgress } from '../build/progress'
import type { DeepReadonly } from '../shared/lib/deep-readonly'

export class ExportError extends Error {
code = 'NEXT_EXPORT_ERROR'
Expand Down Expand Up @@ -188,7 +189,7 @@ export async function exportAppImpl(
!options.pages &&
(require(join(distDir, SERVER_DIRECTORY, PAGES_MANIFEST)) as PagesManifest)

let prerenderManifest: PrerenderManifest | undefined
let prerenderManifest: DeepReadonly<PrerenderManifest> | undefined
try {
prerenderManifest = require(join(distDir, PRERENDER_MANIFEST))
} catch {}
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '../shared/lib/html-context.shared-runtime'
import type { HtmlProps } from '../shared/lib/html-context.shared-runtime'
import { encodeURIPath } from '../shared/lib/encode-uri-path'
import type { DeepReadonly } from '../shared/lib/deep-readonly'

export type { DocumentContext, DocumentInitialProps, DocumentProps }

Expand Down Expand Up @@ -360,7 +361,7 @@ function getAmpPath(ampPath: string, asPath: string): string {
}

function getNextFontLinkTags(
nextFontManifest: NextFontManifest | undefined,
nextFontManifest: DeepReadonly<NextFontManifest> | undefined,
dangerousAsPath: string,
assetPrefix: string = ''
) {
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import {
wrapClientComponentLoader,
} from '../client-component-renderer-logger'
import { createServerModuleMap } from './action-utils'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

export type GetDynamicParamFromSegment = (
// [slug] / [[slug]] / [...slug]
Expand Down Expand Up @@ -137,7 +138,7 @@ export type AppRenderContext = AppRenderBaseContext & {
requestId: string
defaultRevalidate: Revalidate
pagePath: string
clientReferenceManifest: ClientReferenceManifest
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>
assetPrefix: string
flightDataRendererErrorHandler: ErrorHandler
serverComponentsErrorHandler: ErrorHandler
Expand Down
13 changes: 7 additions & 6 deletions packages/next/src/server/app-render/encryption-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ActionManifest } from '../../build/webpack/plugins/flight-client-entry-plugin'
import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

// Keep the key in memory as it should never change during the lifetime of the server in
// both development and production.
Expand Down Expand Up @@ -116,8 +117,8 @@ export function setReferenceManifestsSingleton({
serverActionsManifest,
serverModuleMap,
}: {
clientReferenceManifest: ClientReferenceManifest
serverActionsManifest: ActionManifest
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>
serverActionsManifest: DeepReadonly<ActionManifest>
serverModuleMap: {
[id: string]: {
id: string
Expand Down Expand Up @@ -160,8 +161,8 @@ export function getClientReferenceManifestSingleton() {
const serverActionsManifestSingleton = (globalThis as any)[
SERVER_ACTION_MANIFESTS_SINGLETON
] as {
clientReferenceManifest: ClientReferenceManifest
serverActionsManifest: ActionManifest
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>
serverActionsManifest: DeepReadonly<ActionManifest>
}

if (!serverActionsManifestSingleton) {
Expand All @@ -181,8 +182,8 @@ export async function getActionEncryptionKey() {
const serverActionsManifestSingleton = (globalThis as any)[
SERVER_ACTION_MANIFESTS_SINGLETON
] as {
clientReferenceManifest: ClientReferenceManifest
serverActionsManifest: ActionManifest
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>
serverActionsManifest: DeepReadonly<ActionManifest>
}

if (!serverActionsManifestSingleton) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

/**
* Get external stylesheet link hrefs based on server CSS manifest.
*/
export function getLinkAndScriptTags(
clientReferenceManifest: ClientReferenceManifest,
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>,
filePath: string,
injectedCSS: Set<string>,
injectedScripts: Set<string>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { NextFontManifest } from '../../build/webpack/plugins/next-font-manifest-plugin'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

/**
* Get hrefs for fonts to preload
Expand All @@ -8,7 +9,7 @@ import type { NextFontManifest } from '../../build/webpack/plugins/next-font-man
* Returns null if there are fonts but none to preload and at least some were previously preloaded
*/
export function getPreloadableFonts(
nextFontManifest: NextFontManifest | undefined,
nextFontManifest: DeepReadonly<NextFontManifest> | undefined,
filePath: string | undefined,
injectedFontPreloadTags: Set<string>
): string[] | null {
Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/server/app-render/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ParsedUrlQuery } from 'querystring'
import type { AppPageModule } from '../future/route-modules/app-page/module'
import type { SwrDelta } from '../lib/revalidate'
import type { LoadingModuleData } from '../../shared/lib/app-router-context.shared-runtime'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

import s from 'next/dist/compiled/superstruct'

Expand Down Expand Up @@ -126,14 +127,14 @@ export interface RenderOptsPartial {
buildId: string
basePath: string
trailingSlash: boolean
clientReferenceManifest?: ClientReferenceManifest
clientReferenceManifest?: DeepReadonly<ClientReferenceManifest>
supportsDynamicHTML: boolean
runtime?: ServerRuntime
serverComponents?: boolean
enableTainting?: boolean
assetPrefix?: string
crossOrigin?: '' | 'anonymous' | 'use-credentials' | undefined
nextFontManifest?: NextFontManifest
nextFontManifest?: DeepReadonly<NextFontManifest>
isBot?: boolean
incrementalCache?: import('../lib/incremental-cache').IncrementalCache
isRevalidate?: boolean
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/server/app-render/use-flight-response.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight
import type { BinaryStreamOf } from './app-render'

import { htmlEscapeJsonString } from '../htmlescape'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'

Expand All @@ -18,7 +19,7 @@ const encoder = new TextEncoder()
*/
export function useFlightStream<T>(
flightStream: BinaryStreamOf<T>,
clientReferenceManifest: ClientReferenceManifest,
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>,
nonce?: string
): Promise<T> {
const response = flightResponses.get(flightStream)
Expand Down
13 changes: 8 additions & 5 deletions packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ import { NextDataPathnameNormalizer } from './future/normalizers/request/next-da
import { getIsServerAction } from './lib/server-action-request-meta'
import { isInterceptionRouteAppPath } from './future/helpers/interception-routes'
import { toRoute } from './lib/to-route'
import type { DeepReadonly } from '../shared/lib/deep-readonly'

export type FindComponentsResult = {
components: LoadComponentsReturnType
Expand Down Expand Up @@ -285,9 +286,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
protected readonly renderOpts: BaseRenderOpts
protected readonly serverOptions: Readonly<ServerOptions>
protected readonly appPathRoutes?: Record<string, string[]>
protected readonly clientReferenceManifest?: ClientReferenceManifest
protected readonly clientReferenceManifest?: DeepReadonly<ClientReferenceManifest>
protected interceptionRoutePatterns: RegExp[]
protected nextFontManifest?: NextFontManifest
protected nextFontManifest?: DeepReadonly<NextFontManifest>
private readonly responseCache: ResponseCacheBase

protected abstract getPublicDir(): string
Expand All @@ -314,9 +315,11 @@ export default abstract class Server<ServerOptions extends Options = Options> {
shouldEnsure?: boolean
url?: string
}): Promise<FindComponentsResult | null>
protected abstract getFontManifest(): FontManifest | undefined
protected abstract getPrerenderManifest(): PrerenderManifest
protected abstract getNextFontManifest(): NextFontManifest | undefined
protected abstract getFontManifest(): DeepReadonly<FontManifest> | undefined
protected abstract getPrerenderManifest(): DeepReadonly<PrerenderManifest>
protected abstract getNextFontManifest():
| DeepReadonly<NextFontManifest>
| undefined
protected abstract attachRequestMeta(
req: BaseNextRequest,
parsedUrl: NextUrlWithParsedQuery
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { AppConfig } from '../../../../build/utils'
import type { NextRequest } from '../../../web/spec-extension/request'
import type { PrerenderManifest } from '../../../../build'
import type { NextURL } from '../../../web/next-url'
import type { DeepReadonly } from '../../../../shared/lib/deep-readonly'

import {
RouteModule,
Expand Down Expand Up @@ -63,7 +64,7 @@ export type AppRouteModule =
*/
export interface AppRouteRouteHandlerContext extends RouteModuleHandleContext {
renderOpts: StaticGenerationContext['renderOpts']
prerenderManifest: PrerenderManifest
prerenderManifest: DeepReadonly<PrerenderManifest>
}

/**
Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/server/lib/incremental-cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
IncrementalCacheKindHint,
} from '../../response-cache'
import type { Revalidate } from '../revalidate'
import type { DeepReadonly } from '../../../shared/lib/deep-readonly'

import FetchCache from './fetch-cache'
import FileSystemCache from './file-system-cache'
Expand Down Expand Up @@ -67,7 +68,7 @@ export class IncrementalCache implements IncrementalCacheType {
readonly disableForTestmode?: boolean
readonly cacheHandler?: CacheHandler
readonly hasCustomCacheHandler: boolean
readonly prerenderManifest: PrerenderManifest
readonly prerenderManifest: DeepReadonly<PrerenderManifest>
readonly requestHeaders: Record<string, undefined | string | string[]>
readonly requestProtocol?: 'http' | 'https'
readonly allowedRevalidateHeaderKeys?: string[]
Expand Down Expand Up @@ -115,7 +116,7 @@ export class IncrementalCache implements IncrementalCacheType {
allowedRevalidateHeaderKeys?: string[]
requestHeaders: IncrementalCache['requestHeaders']
maxMemoryCacheSize?: number
getPrerenderManifest: () => PrerenderManifest
getPrerenderManifest: () => DeepReadonly<PrerenderManifest>
fetchCacheKeyPrefix?: string
CurCacheHandler?: typeof CacheHandler
experimental: { ppr: boolean }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PrerenderManifest } from '../../../build'
import type { DeepReadonly } from '../../../shared/lib/deep-readonly'
import type { Revalidate } from '../revalidate'

/**
Expand All @@ -18,7 +19,9 @@ export class SharedRevalidateTimings {
* The prerender manifest that contains the initial revalidate timings for
* routes.
*/
private readonly prerenderManifest: Pick<PrerenderManifest, 'routes'>
private readonly prerenderManifest: DeepReadonly<
Pick<PrerenderManifest, 'routes'>
>
) {}

/**
Expand Down
Loading

0 comments on commit d90fc32

Please sign in to comment.