Skip to content

Commit

Permalink
Merge branch 'canary' into fix/typescript-get-img-props-alt-text
Browse files Browse the repository at this point in the history
  • Loading branch information
icyJoseph authored Sep 30, 2024
2 parents 0367768 + b8d1ef7 commit b00824b
Show file tree
Hide file tree
Showing 41 changed files with 859 additions and 222 deletions.
5 changes: 0 additions & 5 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,11 +1073,6 @@ impl NextConfig {
Vc::cell(self.experimental.taint.unwrap_or(false))
}

#[turbo_tasks::function]
pub fn enable_dynamic_io(&self) -> Vc<bool> {
Vc::cell(self.experimental.dynamic_io.unwrap_or(false))
}

#[turbo_tasks::function]
pub fn use_swc_css(&self) -> Vc<bool> {
Vc::cell(
Expand Down
21 changes: 7 additions & 14 deletions crates/next-core/src/next_import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,12 @@ pub async fn get_next_client_import_map(
match ty.into_value() {
ClientContextType::Pages { .. } => {}
ClientContextType::App { app_dir } => {
let react_flavor = if *next_config.enable_ppr().await?
|| *next_config.enable_taint().await?
|| *next_config.enable_dynamic_io().await?
{
"-experimental"
} else {
""
};
let react_flavor =
if *next_config.enable_ppr().await? || *next_config.enable_taint().await? {
"-experimental"
} else {
""
};

import_map.insert_exact_alias(
"react",
Expand Down Expand Up @@ -686,12 +684,7 @@ async fn rsc_aliases(
) -> Result<()> {
let ppr = *next_config.enable_ppr().await?;
let taint = *next_config.enable_taint().await?;
let dynamic_io = *next_config.enable_dynamic_io().await?;
let react_channel = if ppr || taint || dynamic_io {
"-experimental"
} else {
""
};
let react_channel = if ppr || taint { "-experimental" } else { "" };
let react_client_package = get_react_client_package(&next_config).await?;

let mut alias = IndexMap::new();
Expand Down
38 changes: 19 additions & 19 deletions docs/02-app/01-building-your-application/04-caching/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Caching behavior changes depending on whether the route is statically or dynamic

## Request Memoization

Next.js extends the [`fetch` API](#fetch) to automatically **memoize** requests that have the same URL and options. This means you can call a fetch function for the same data in multiple places in a React component tree while only executing it once.
React extends the [`fetch` API](#fetch) to automatically **memoize** requests that have the same URL and options. This means you can call a fetch function for the same data in multiple places in a React component tree while only executing it once.

<Image
alt="Deduplicated Fetch Requests"
Expand Down Expand Up @@ -372,24 +372,24 @@ When configuring the different caching mechanisms, it's important to understand

The following table provides an overview of how different Next.js APIs affect caching:

| API | Router Cache | Full Route Cache | Data Cache | React Cache |
| ----------------------------------------------------------------------- | -------------------------- | --------------------- | --------------------- | ----------- |
| [`<Link prefetch>`](#link) | Cache | | | |
| [`router.prefetch`](#routerprefetch) | Cache | | | |
| [`router.refresh`](#routerrefresh) | Revalidate | | | |
| [`fetch`](#fetch) | | | Cache | Cache |
| [`fetch` `options.cache`](#fetch-optionscache) | | | Cache or Opt out | |
| [`fetch` `options.next.revalidate`](#fetch-optionsnextrevalidate) | | Revalidate | Revalidate | |
| [`fetch` `options.next.tags`](#fetch-optionsnexttags-and-revalidatetag) | | Cache | Cache | |
| [`revalidateTag`](#fetch-optionsnexttags-and-revalidatetag) | Revalidate (Server Action) | Revalidate | Revalidate | |
| [`revalidatePath`](#revalidatepath) | Revalidate (Server Action) | Revalidate | Revalidate | |
| [`const revalidate`](#segment-config-options) | | Revalidate or Opt out | Revalidate or Opt out | |
| [`const dynamic`](#segment-config-options) | | Cache or Opt out | Cache or Opt out | |
| [`cookies`](#cookies) | Revalidate (Server Action) | Opt out | | |
| [`headers`, `searchParams`](#dynamic-functions) | | Opt out | | |
| [`generateStaticParams`](#generatestaticparams) | | Cache | | |
| [`React.cache`](#react-cache-function) | | | | Cache |
| [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) | | | Cache | |
| API | Router Cache | Full Route Cache | Data Cache | React Request Memoization |
| ----------------------------------------------------------------------- | -------------------------- | --------------------- | --------------------- | ------------------------- |
| [`<Link prefetch>`](#link) | Cache | | | |
| [`router.prefetch`](#routerprefetch) | Cache | | | |
| [`router.refresh`](#routerrefresh) | Revalidate | | | |
| [`fetch`](#fetch) | | | Cache | Cache |
| [`fetch` `options.cache`](#fetch-optionscache) | | | Cache or Opt out | |
| [`fetch` `options.next.revalidate`](#fetch-optionsnextrevalidate) | | Revalidate | Revalidate | |
| [`fetch` `options.next.tags`](#fetch-optionsnexttags-and-revalidatetag) | | Cache | Cache | |
| [`revalidateTag`](#fetch-optionsnexttags-and-revalidatetag) | Revalidate (Server Action) | Revalidate | Revalidate | |
| [`revalidatePath`](#revalidatepath) | Revalidate (Server Action) | Revalidate | Revalidate | |
| [`const revalidate`](#segment-config-options) | | Revalidate or Opt out | Revalidate or Opt out | |
| [`const dynamic`](#segment-config-options) | | Cache or Opt out | Cache or Opt out | |
| [`cookies`](#cookies) | Revalidate (Server Action) | Opt out | | |
| [`headers`, `searchParams`](#dynamic-functions) | | Opt out | | |
| [`generateStaticParams`](#generatestaticparams) | | Cache | | |
| [`React.cache`](#react-cache-function) | | | | Cache |
| [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) | | | Cache | |

### `<Link>`

Expand Down
2 changes: 2 additions & 0 deletions docs/03-pages/02-api-reference/01-components/head.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export default IndexPage

In this case only the second `<meta property="og:title" />` is rendered. `meta` tags with duplicate `key` attributes are automatically handled.

> **Good to know**: `<title>` and `<base>` tags are automatically checked for duplicates by Next.js, so using key is not necessary for these tags.
> The contents of `head` get cleared upon unmounting the component, so make sure each page completely defines what it needs in `head`, without making assumptions about what other pages added.
## Use minimal nesting
Expand Down
51 changes: 51 additions & 0 deletions errors/next-request-in-use-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: Cannot access `cookies()` or `headers()` in `"use cache"`
---

#### Why This Error Occurred

A function is trying to read from the current incoming request inside the scope of a function annotated with `"use cache"`. This is not supported because it would make the cache invalidated by every request which is probably not what you intended.

#### Possible Ways to Fix It

Instead of calling this inside the `"use cache"` function, move it outside the function and pass the value in as an argument. The specific value will now be part of the cache key through its arguments.

Before:

```jsx filename="app/page.js"
import { cookies } from 'next/headers'

async function getExampleData() {
"use cache"
- const isLoggedIn = (await cookies()).has('token')
...
}

export default async function Page() {
const data = await getExampleData()
return ...
}
```

After:

```jsx filename="app/page.js"
import { cookies } from 'next/headers'

async function getExampleData(isLoggedIn) {
"use cache"
...
}

export default async function Page() {
+ const isLoggedIn = (await cookies()).has('token')
const data = await getExampleData(isLoggedIn)
return ...
}
```

### Useful Links

- [`headers()` function](https://nextjs.org/docs/app/api-reference/functions/headers)
- [`cookies()` function](https://nextjs.org/docs/app/api-reference/functions/cookies)
- [`draftMode()` function](https://nextjs.org/docs/app/api-reference/functions/draft-mode)
1 change: 1 addition & 0 deletions packages/next/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export { URLPattern } from 'next/dist/compiled/@edge-runtime/primitives/url'
export { ImageResponse } from 'next/dist/server/web/spec-extension/image-response'
export type { ImageResponseOptions } from 'next/dist/compiled/@vercel/og/types'
export { unstable_after } from 'next/dist/server/after'
export { connection } from 'next/dist/server/request/connection'
export type { UnsafeUnwrappedSearchParams } from 'next/dist/server/request/search-params'
export type { UnsafeUnwrappedParams } from 'next/dist/server/request/params'
2 changes: 2 additions & 0 deletions packages/next/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const serverExports = {
URLPattern: require('next/dist/server/web/spec-extension/url-pattern')
.URLPattern,
unstable_after: require('next/dist/server/after').unstable_after,
connection: require('next/dist/server/request/connection').connection,
}

// https://nodejs.org/api/esm.html#commonjs-namespaces
Expand All @@ -26,3 +27,4 @@ exports.userAgentFromString = serverExports.userAgentFromString
exports.userAgent = serverExports.userAgent
exports.URLPattern = serverExports.URLPattern
exports.unstable_after = serverExports.unstable_after
exports.connection = serverExports.connection
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type { DeepReadonly } from '../../shared/lib/deep-readonly'
import type { AfterContext } from '../../server/after/after-context'
import type { ServerComponentsHmrCache } from '../../server/response-cache'

import { cacheAsyncStorage } from '../../server/app-render/cache-async-storage.external'

export interface RequestStore {
/**
* The URL of the request. This only specifies the pathname and the search
Expand Down Expand Up @@ -48,6 +50,11 @@ export { requestAsyncStorage }
export function getExpectedRequestStore(callingExpression: string) {
const store = requestAsyncStorage.getStore()
if (store) return store
if (cacheAsyncStorage.getStore()) {
throw new Error(
`\`${callingExpression}\` cannot be called inside "use cache". Call it outside and pass an argument instead. Read more: https://nextjs.org/docs/messages/next-request-in-use-cache`
)
}
throw new Error(
`\`${callingExpression}\` was called outside a request scope. Read more: https://nextjs.org/docs/messages/next-dynamic-api-wrong-context`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export function addSearchParamsToPageSegments(
// If it's a page segment, modify the segment by adding search params
if (segment.includes(PAGE_SEGMENT_KEY)) {
const newSegment = addSearchParamsIfPageSegment(segment, searchParams)
console.log({ existingSegment: segment, newSegment })
return [newSegment, parallelRoutes, ...rest]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import type {
ChildSegmentMap,
ReadyCacheNode,
} from '../../../shared/lib/app-router-context.shared-runtime'
import {
DEFAULT_SEGMENT_KEY,
PAGE_SEGMENT_KEY,
} from '../../../shared/lib/segment'
import { DEFAULT_SEGMENT_KEY } from '../../../shared/lib/segment'
import { matchSegment } from '../match-segments'
import { createRouterCacheKey } from './create-router-cache-key'
import type { FetchServerResponseResult } from './fetch-server-response'
Expand Down Expand Up @@ -124,27 +121,13 @@ export function updateCacheNodeOnNavigation(
const oldSegmentChild =
oldRouterStateChild !== undefined ? oldRouterStateChild[0] : undefined

// A dynamic segment will be an array, and doesn't correspond with a page segment.
const isPageSegment = Array.isArray(newSegmentChild)
? false
: // A page segment might contain search parameters, so we verify that it starts with the page segment key.
newSegmentChild.startsWith(PAGE_SEGMENT_KEY)

const oldCacheNodeChild =
oldSegmentMapChild !== undefined
? oldSegmentMapChild.get(newSegmentKeyChild)
: undefined

let taskChild: Task | null
if (isPageSegment) {
// This is a leaf segment — a page, not a shared layout. We always apply
// its data.
taskChild = spawnPendingTask(
newRouterStateChild,
prefetchDataChild !== undefined ? prefetchDataChild : null,
prefetchHead
)
} else if (newSegmentChild === DEFAULT_SEGMENT_KEY) {
if (newSegmentChild === DEFAULT_SEGMENT_KEY) {
// This is another kind of leaf segment — a default route.
//
// Default routes have special behavior. When there's no matching segment
Expand Down
6 changes: 1 addition & 5 deletions packages/next/src/lib/needs-experimental-react.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { NextConfig } from '../server/config-shared'

export function needsExperimentalReact(config: NextConfig) {
return Boolean(
config.experimental?.ppr ||
config.experimental?.taint ||
config.experimental?.dynamicIO
)
return Boolean(config.experimental?.ppr || config.experimental?.taint)
}
1 change: 1 addition & 0 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ export async function handleAction({
if (isMultipartAction) {
if (isFetchAction) {
const busboy = (require('busboy') as typeof import('busboy'))({
defParamCharset: 'utf8',
headers: req.headers,
limits: { fieldSize: bodySizeLimitBytes },
})
Expand Down
Loading

0 comments on commit b00824b

Please sign in to comment.