Skip to content

Commit

Permalink
Merge branch 'canary' into refine-revalidatePath-warning
Browse files Browse the repository at this point in the history
  • Loading branch information
samcx authored Jan 26, 2024
2 parents 3a44b36 + 9626f31 commit 586364a
Show file tree
Hide file tree
Showing 40 changed files with 590 additions and 287 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ For example, you can intercept the `photo` segment from within the `feed` segmen

### Modals

Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals.
Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals. This allows you to solve common challenges when building modals, such as:

Using this pattern to create modals overcomes some common challenges when working with modals, by allowing you to:
- Making the modal content **shareable through a URL**.
- **Preserving context** when the page is refreshed, instead of closing the modal.
- **Closing the modal on backwards navigation** rather than going to the previous route.
- **Reopening the modal on forwards navigation**.

- Make the modal content **shareable through a URL**
- **Preserve context** when the page is refreshed, instead of closing the modal
- **Close the modal on backwards navigation** rather than going to the previous route
- **Reopen the modal on forwards navigation**
Consider the following UI pattern, where a user can open a photo modal from a gallery using client-side navigation, or navigate to the photo page directly from a shareable URL:

<Image
alt="Intercepting routes modal example"
Expand All @@ -74,8 +74,10 @@ Using this pattern to create modals overcomes some common challenges when workin
height="976"
/>

> In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher.
In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher.

Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal.
See the [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes#modals) documentation for a step-by-step example, or see our [image gallery example](https://github.com/vercel-labs/nextgram).

[View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes.
> **Good to know:**
>
> - Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal.
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,26 @@ description: Next.js allows you to serve static files, like images, in the publi

Next.js can serve static files, like images, under a folder called `public` in the root directory. Files inside `public` can then be referenced by your code starting from the base URL (`/`).

For example, if you add `me.png` inside `public`, the following code will access the image:
For example, the file `public/avatars/me.png` can be viewed by visiting the `/avatars/me.png` path. The code to display that image might look like:

```jsx filename="Avatar.js"
```jsx filename="avatar.js"
import Image from 'next/image'

export function Avatar() {
return <Image src="/me.png" alt="me" width="64" height="64" />
export function Avatar({ id, alt }) {
return <Image src={`/avatars/${id}.png`} alt={alt} width="64" height="64" />
}

export function AvatarOfMe() {
return <Avatar id="me" alt="A portrait of me" />
}
```

## Caching

Next.js automatically adds caching headers to immutable assets in the `public` folder. The default caching headers applied are:
Next.js cannot safely cache assets in the `public` folder because they may change. The default caching headers applied are:

```jsx
Cache-Control: public, max-age=31536000, immutable
Cache-Control: public, max-age=0
```

## Robots, Favicons, and others
Expand Down
2 changes: 2 additions & 0 deletions docs/02-app/02-api-reference/04-functions/revalidatePath.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ description: API Reference for the revalidatePath function.
>
> - `revalidatePath` is available in both [Node.js and Edge runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes).
> - `revalidatePath` only invalidates the cache when the included path is next visited. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
> - Currently, `revalidatePath` invalidates all the routes in the [client-side Router Cache](/docs/app/building-your-application/caching#router-cache). This behavior is temporary and will be updated in the future to apply only to the specific path.
> - Using `revalidatePath` invalidates **only the specific path** in the [server-side Route Cache](/docs/app/building-your-application/caching#full-route-cache).
## Parameters

Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "14.1.1-canary.13"
"version": "14.1.1-canary.14"
}
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"keywords": [
"react",
"next",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"description": "ESLint configuration used by Next.js.",
"main": "index.js",
"license": "MIT",
Expand All @@ -10,7 +10,7 @@
},
"homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config",
"dependencies": {
"@next/eslint-plugin-next": "14.1.1-canary.13",
"@next/eslint-plugin-next": "14.1.1-canary.14",
"@rushstack/eslint-patch": "^1.3.3",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0",
"eslint-import-resolver-node": "^0.3.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/font/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/font",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-mdx/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-plugin-storybook/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-module/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-nomodule/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-swc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/swc",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"private": true,
"scripts": {
"clean": "node ../../scripts/rm.mjs native",
Expand Down
14 changes: 7 additions & 7 deletions packages/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next",
"version": "14.1.1-canary.13",
"version": "14.1.1-canary.14",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
Expand Down Expand Up @@ -92,7 +92,7 @@
]
},
"dependencies": {
"@next/env": "14.1.1-canary.13",
"@next/env": "14.1.1-canary.14",
"@swc/helpers": "0.5.2",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
Expand Down Expand Up @@ -146,11 +146,11 @@
"@mswjs/interceptors": "0.23.0",
"@napi-rs/cli": "2.16.2",
"@napi-rs/triples": "1.1.0",
"@next/polyfill-module": "14.1.1-canary.13",
"@next/polyfill-nomodule": "14.1.1-canary.13",
"@next/react-dev-overlay": "14.1.1-canary.13",
"@next/react-refresh-utils": "14.1.1-canary.13",
"@next/swc": "14.1.1-canary.13",
"@next/polyfill-module": "14.1.1-canary.14",
"@next/polyfill-nomodule": "14.1.1-canary.14",
"@next/react-dev-overlay": "14.1.1-canary.14",
"@next/react-refresh-utils": "14.1.1-canary.14",
"@next/swc": "14.1.1-canary.14",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "^1.35.1",
"@taskr/clear": "1.1.0",
Expand Down
44 changes: 29 additions & 15 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ async function createTreeCodeFromPath(
}

for (const [parallelKey, parallelSegment] of parallelSegments) {
// if parallelSegment is the page segment (ie, `page$` and not ['page$']), it gets loaded into the __PAGE__ slot
// as it's the page for the current route.
if (parallelSegment === PAGE_SEGMENT) {
const matchedPagePath = `${appDirPrefix}${segmentPath}${
parallelKey === 'children' ? '' : `/${parallelKey}`
Expand All @@ -293,27 +295,37 @@ async function createTreeCodeFromPath(
continue
}

// if the parallelSegment was not matched to the __PAGE__ slot, then it's a parallel route at this level.
// the code below recursively traverses the parallel slots directory to match the corresponding __PAGE__ for each parallel slot
// while also filling in layout/default/etc files into the loader tree at each segment level.

const subSegmentPath = [...segments]
if (parallelKey !== 'children') {
// A `children` parallel key should have already been processed in the above segment
// So we exclude it when constructing the subsegment path for the remaining segment levels
subSegmentPath.push(parallelKey)
}

const normalizedParallelSegments = Array.isArray(parallelSegment)
? parallelSegment.slice(0, 1)
: [parallelSegment]
const normalizedParallelSegment = Array.isArray(parallelSegment)
? parallelSegment[0]
: parallelSegment

subSegmentPath.push(
...normalizedParallelSegments.filter(
(segment) =>
segment !== PAGE_SEGMENT && segment !== PARALLEL_CHILDREN_SEGMENT
)
)
if (
normalizedParallelSegment !== PAGE_SEGMENT &&
normalizedParallelSegment !== PARALLEL_CHILDREN_SEGMENT
) {
// If we don't have a page segment, nor a special $children marker, it means we need to traverse the next directory
// (ie, `normalizedParallelSegment` would correspond with the folder that contains the next level of pages/layout/etc)
// we push it to the subSegmentPath so that we can fill in the loader tree for that segment.
subSegmentPath.push(normalizedParallelSegment)
}

const { treeCode: pageSubtreeCode } =
await createSubtreePropsFromSegmentPath(subSegmentPath)

const parallelSegmentPath = subSegmentPath.join('/')

// Fill in the loader tree for all of the special files types (layout, default, etc) at this level
// `page` is not included here as it's added above.
const filePaths = await Promise.all(
Object.values(FILE_TYPES).map(async (file) => {
Expand Down Expand Up @@ -534,14 +546,15 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
const isParallelRoute = rest[0].startsWith('@')
if (isParallelRoute) {
if (rest.length === 2 && rest[1] === 'page') {
// matched will be an empty object in case the parallel route is at a path with no existing page
// in which case, we need to mark it as a regular page segment
matched[rest[0]] = Object.keys(matched).length
? [PAGE_SEGMENT]
: PAGE_SEGMENT
// We found a parallel route at this level. We don't want to mark it explicitly as the page segment,
// as that should be matched to the `children` slot. Instead, we use an array, to signal to `createSubtreePropsFromSegmentPath`
// that it needs to recursively fill in the loader tree code for the parallel route at the appropriate levels.
matched[rest[0]] = [PAGE_SEGMENT]
continue
}
// we insert a special marker in order to also process layout/etc files at the slot level
// If it was a parallel route but we weren't able to find the page segment (ie, maybe the page is nested further)
// we first insert a special marker to ensure that we still process layout/default/etc at the slot level prior to continuing
// on to the page segment.
matched[rest[0]] = [PARALLEL_CHILDREN_SEGMENT, ...rest.slice(1)]
continue
}
Expand Down Expand Up @@ -573,6 +586,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
matched.children = rest[0]
}
}

return Object.entries(matched)
}

Expand Down
Loading

0 comments on commit 586364a

Please sign in to comment.