Skip to content
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
53 changes: 14 additions & 39 deletions packages/next/src/build/segment-config/app/app-segment-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ const RuntimeSampleSchema = z
})
.strict()

const InstantConfigStaticSchema = z
const InstantConfigObjectSchema = z
.object({
prefetch: z.literal('static'),
samples: z.array(RuntimeSampleSchema).min(1).optional(),
from: z.array(z.string()).optional(),
unstable_disableValidation: z.literal(true).optional(),
Expand All @@ -30,22 +29,9 @@ const InstantConfigStaticSchema = z
})
.strict()

const InstantConfigRuntimeSchema = z
.object({
prefetch: z.literal('runtime'),
samples: z.array(RuntimeSampleSchema).min(1),
from: z.array(z.string()).optional(),
unstable_disableValidation: z.literal(true).optional(),
unstable_disableDevValidation: z.literal(true).optional(),
unstable_disableBuildValidation: z.literal(true).optional(),
})
.strict()

const InstantConfigSchema = z.union([
z.discriminatedUnion('prefetch', [
InstantConfigStaticSchema,
InstantConfigRuntimeSchema,
]),
InstantConfigObjectSchema,
z.literal(true),
z.literal(false),
])

Expand All @@ -56,7 +42,7 @@ const PrefetchSchema = z.enum([
'force-runtime',
])

export type Instant = InstantConfigStatic | InstantConfigRuntime | false
export type Instant = InstantConfig | true | false

export type Prefetch =
| 'auto'
Expand All @@ -65,46 +51,35 @@ export type Prefetch =
| 'force-runtime'

export type InstantConfigForTypeCheckInternal = __GenericInstantConfig | Instant
// the __GenericPrefetch type is used to avoid type widening issues with
// the __GenericInstantConfig type is used to avoid type widening issues with
// our choice to make exports the medium for programming a Next.js application
// With exports the type is controlled by the module and all we can do is assert on it
// from a consumer. However with string literals in objects these are by default typed widely
// and thus cannot match the discriminated union type. If we figure out a better way we should
// delete the __GenericPrefetch member.
// delete the __GenericInstantConfig member.
interface __GenericInstantConfig {
prefetch: string
samples?: Array<WideInstantSample>
from?: string[]
unstable_disableValidation?: boolean
unstable_disableDevValidation?: boolean
unstable_disableBuildValidation?: boolean
}

interface InstantConfigStatic {
prefetch: 'static'
samples?: Array<InstantSample>
from?: string[]
unstable_disableValidation?: true
unstable_disableDevValidation?: true
unstable_disableBuildValidation?: true
type WideInstantSample = {
cookies?: InstantSample['cookies']
headers?: Array<string[]>
params?: InstantSample['params']
searchParams?: InstantSample['searchParams']
}

interface InstantConfigRuntime {
prefetch: 'runtime'
samples: Array<InstantSample>
export interface InstantConfig {
samples?: Array<InstantSample>
from?: string[]
unstable_disableValidation?: true
unstable_disableDevValidation?: true
unstable_disableBuildValidation?: true
}

type WideInstantSample = {
cookies?: InstantSample['cookies']
headers?: Array<string[]>
params?: InstantSample['params']
searchParams?: InstantSample['searchParams']
}

export type InstantSample = {
cookies?: Array<{
name: string
Expand Down Expand Up @@ -212,7 +187,7 @@ export function parseAppSegmentConfig(
case 'unstable_instant': {
return {
// @TODO replace this link with a link to the docs when they are written
message: `Invalid unstable_instant value ${JSON.stringify(ctx.data)} on "${route}", must be an object with \`prefetch: "static"\` or \`prefetch: "runtime"\`, or \`false\`. Read more at https://nextjs.org/docs/messages/invalid-instant-configuration`,
message: `Invalid unstable_instant value ${JSON.stringify(ctx.data)} on "${route}", must be \`true\`, \`false\`, or an object. Read more at https://nextjs.org/docs/messages/invalid-instant-configuration`,
}
}
case 'unstable_prefetch': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ async function createFlightRouterStateFromLoaderTreeImpl(
prefetchHints |= PrefetchHint.IsRootLayout
}

if (instantConfig && typeof instantConfig === 'object') {
if (
instantConfig === true ||
(typeof instantConfig === 'object' && instantConfig !== null)
) {
prefetchHints |= PrefetchHint.SubtreeHasInstant
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ export async function isPageAllowedToBlock(tree: LoaderTree): Promise<boolean> {
// instant UI, so we should make sure that a static shell exists.
// (even if it'd use runtime prefetching for client navs)
if (instantConfig !== undefined) {
if (typeof instantConfig === 'object') {
return false
} else if (instantConfig === false) {
if (instantConfig === false) {
return true
} else {
return false
}
}

Expand Down Expand Up @@ -84,12 +84,14 @@ async function anySegmentNeedsInstantValidation(
): Promise<boolean> {
const segments = await findSegmentsWithInstantConfig(rootTree)

// Check if there's any configs with `prefetch: 'static'` or `mode: 'instant'`.
// Check if there's any non-false configs that need validation.
// (If there's only `false`, there's no need to run validation).
// If any segment has `unstable_disableValidation`, we skip validation for the whole tree.
let needsValidation = false
for (const { config } of segments) {
if (typeof config === 'object') {
if (config === true) {
needsValidation = true
} else if (typeof config === 'object') {
if (
config.unstable_disableValidation === true ||
(mode === 'dev' && config.unstable_disableDevValidation === true) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,10 @@ export async function createCombinedPayloadAtDepth(
(layoutOrPageMod as AppSegmentConfig).unstable_instant ?? null
prefetchConfig =
(layoutOrPageMod as AppSegmentConfig).unstable_prefetch ?? null
if (instantConfig && typeof instantConfig === 'object') {
if (
instantConfig === true ||
(typeof instantConfig === 'object' && instantConfig !== null)
) {
const rawFactory: unknown = (layoutOrPageMod as any)
.__debugCreateInstantConfigStack
localCreateInstantStack =
Expand Down Expand Up @@ -1243,7 +1246,10 @@ export async function createCombinedPayloadAtDepth(
requiresInstantUI = false
createInstantStack = null
configDepth = -1
} else if (instantConfig && typeof instantConfig === 'object') {
} else if (
instantConfig === true ||
(typeof instantConfig === 'object' && instantConfig !== null)
) {
requiresInstantUI = true
createInstantStack = localCreateInstantStack
configDepth = segmentDepth
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/server/typescript/rules/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ const API_DOCS: Record<
link: 'https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#maxduration',
},
unstable_instant: {
description: `Specifies the default prefetching behavior for this segment. This configuration is currently under development and will change.`,
description: `Enables instant navigation validation for this segment. This configuration is currently under development and will change.`,
link: '(docs coming soon)',
type: 'object | false',
type: 'true | object | false',
// TODO: ideally, we'd validate the config object somehow, but this is difficult to do
// with the way this plugin is currently structured.
// For now, since we don't provide an `options` here, we won't do any validation in
// `getSemanticDiagnosticsForExportVariableStatement` below, and only provide hover a tooltip + autocomplete.
insertText: 'unstable_instant = { prefetch: "static" };',
insertText: 'unstable_instant = true;',
},
unstable_prefetch: {
description: `Controls prefetching behavior for this segment. This configuration is currently under development and will change.`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CachedData } from '../../data-fetching'
import { connection } from 'next/server'
import { Suspense } from 'react'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = __dirname + '/__PAGE__'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { UncachedFetch, CachedData } from '../data-fetching'
import { PrivateCachedData } from './data-fetching'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = '/private-cache/__LAYOUT__'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { UncachedFetch, CachedData } from '../data-fetching'
import { ShortLivedCache } from './data-fetching'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = __dirname + '/__LAYOUT__'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Suspense } from 'react'
import { UncachedFetch, CachedFetch, CachedData } from '../data-fetching'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = __dirname + '/__LAYOUT__'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { CachedData, getCachedData } from '../../data-fetching'
import { cookies } from 'next/headers'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = __dirname + '/__PAGE__'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CachedData, getCachedData } from '../../data-fetching'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

const CACHE_KEY = __dirname + '/__PAGE__'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Suspense } from 'react'
import { Static, Runtime, Dynamic } from '../shared'

export const unstable_instant = { prefetch: 'runtime', samples: [{}] }
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

export default function Root({ children }: { children: React.ReactNode }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Suspense } from 'react'
import { connection } from 'next/server'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ searchParams: { myParam: 'testValue' } }],
}
export const unstable_prefetch = 'force-runtime'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Instant } from 'next'
import { ThrowsInClient } from './client'

export const unstable_instant: Instant = {
prefetch: 'static',
samples: [{ searchParams: {} }],
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Instant } from 'next'
import { ThrowsInClient } from './client'

export const unstable_instant: Instant = {
prefetch: 'static',
samples: [{ searchParams: {} }],
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { cookies } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ cookies: [] }],
}
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { cookies } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ cookies: [] }],
}
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { cookies } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ cookies: [] }],
}
export const unstable_instant = true
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import assert from 'node:assert'
import { Suspense } from 'react'

export const unstable_instant: Instant = {
prefetch: 'runtime',
samples: [
{
cookies: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { cookies } from 'next/headers'
import assert from 'node:assert/strict'

export const unstable_instant: Instant = {
prefetch: 'runtime',
samples: [
{
cookies: [{ name: 'testCookie', value: 'testValue' }],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { cookies } from 'next/headers'
import { ClientChild } from './client'

export const unstable_instant: Instant = {
prefetch: 'runtime',
samples: [
{
cookies: [{ name: 'testCookie', value: 'testValue' }],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { AssertParamsClient } from './client'
// samples use 'hello', but generateStaticParams uses 'foo'/'bar'.
// During validation, the sample params should be used, not the GSP values.
export const unstable_instant: Instant = {
prefetch: 'runtime',
samples: [
{
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { headers } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ headers: [] }],
}
export const unstable_instant = { samples: [{ headers: [] }] }
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { headers } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ headers: [] }],
}
export const unstable_instant = { samples: [{ headers: [] }] }
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { headers } from 'next/headers'
import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'

export const unstable_instant = {
prefetch: 'runtime',
samples: [{ headers: [] }],
}
export const unstable_instant = { samples: [{ headers: [] }] }
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import assert from 'node:assert'
import { Suspense } from 'react'

export const unstable_instant: Instant = {
prefetch: 'runtime',
samples: [
{
headers: [
Expand Down
Loading
Loading