Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vercel/next.js
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v14.2.10
Choose a base ref
...
head repository: vercel/next.js
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: bfbc92aab5c727444ed21e0b84bd55cda2e22067
Choose a head ref
  • 4 commits
  • 33 files changed
  • 4 contributors

Commits on Sep 12, 2024

  1. Update revalidateTag to batch tags in one request (#65296)

    As discussed this collects all `revalidateTag` calls and invokes in a
    single request to avoid race conditions and overhead from multiple
    pending requests.
    
    x-ref: [slack
    thread](https://vercel.slack.com/archives/C0676QZBWKS/p1714688045037509?thread_ts=1710902198.529179&cid=C0676QZBWKS)
    
    Closes NEXT-3306
    ijjk committed Sep 12, 2024
    Copy the full SHA
    9954a21 View commit details
  2. fix: setting assetPrefix to URL format breaks HMR (#70040)

    Backporting:
    - #68622
    - #68681
    - #68518
    devjiwonchoi authored Sep 12, 2024
    Copy the full SHA
    276ddf3 View commit details
  3. fix: correct metadata url suffix (#69959) (#70042)

    Backporting #69959
    
    Co-authored-by: Maikel <maikel.van.dort@gmail.com>
    devjiwonchoi and Netail authored Sep 12, 2024
    Copy the full SHA
    fa51ff5 View commit details
  4. v14.2.11

    vercel-release-bot committed Sep 12, 2024
    Copy the full SHA
    bfbc92a View commit details
Showing with 233 additions and 82 deletions.
  1. +16 −0 docs/02-app/02-api-reference/04-functions/generate-metadata.mdx
  2. +17 −8 docs/02-app/02-api-reference/05-next-config-js/assetPrefix.mdx
  3. +1 −1 lerna.json
  4. +1 −1 packages/create-next-app/package.json
  5. +2 −2 packages/eslint-config-next/package.json
  6. +1 −1 packages/eslint-plugin-next/package.json
  7. +1 −1 packages/font/package.json
  8. +1 −1 packages/next-bundle-analyzer/package.json
  9. +1 −1 packages/next-codemod/package.json
  10. +1 −1 packages/next-env/package.json
  11. +1 −1 packages/next-mdx/package.json
  12. +1 −1 packages/next-plugin-storybook/package.json
  13. +1 −1 packages/next-polyfill-module/package.json
  14. +1 −1 packages/next-polyfill-nomodule/package.json
  15. +1 −1 packages/next-swc/package.json
  16. +6 −6 packages/next/package.json
  17. +8 −8 packages/next/src/client/components/react-dev-overlay/internal/helpers/get-socket-url.ts
  18. +9 −2 packages/next/src/lib/metadata/generate/meta.tsx
  19. +18 −9 packages/next/src/server/app-render/action-handler.ts
  20. +6 −3 packages/next/src/server/app-render/app-render.tsx
  21. +8 −5 packages/next/src/server/future/route-modules/app-route/module.ts
  22. +8 −0 packages/next/src/server/lib/router-server.ts
  23. +0 −9 packages/next/src/server/web/spec-extension/revalidate.ts
  24. +44 −0 packages/next/src/shared/lib/normalized-asset-prefix.test.ts
  25. +11 −8 packages/next/src/shared/lib/normalized-asset-prefix.ts
  26. +1 −1 packages/react-refresh-utils/package.json
  27. +2 −2 packages/third-parties/package.json
  28. +7 −7 pnpm-lock.yaml
  29. +7 −0 test/development/app-dir/hmr-asset-prefix-full-url/app/layout.tsx
  30. +3 −0 test/development/app-dir/hmr-asset-prefix-full-url/app/page.tsx
  31. +32 −0 test/development/app-dir/hmr-asset-prefix-full-url/asset-prefix.test.ts
  32. +12 −0 test/e2e/app-dir/metadata/app/opengraph/page.tsx
  33. +4 −0 test/e2e/app-dir/metadata/metadata.test.ts
16 changes: 16 additions & 0 deletions docs/02-app/02-api-reference/04-functions/generate-metadata.mdx
Original file line number Diff line number Diff line change
@@ -490,6 +490,18 @@ export const metadata = {
alt: 'My custom alt',
},
],
videos: [
{
url: 'https://nextjs.org/video.mp4', // Must be an absolute URL
width: 800,
height: 600,
},
],
audio: [
{
url: 'https://nextjs.org/audio.mp3', // Must be an absolute URL
},
],
locale: 'en_US',
type: 'website',
},
@@ -509,6 +521,10 @@ export const metadata = {
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:video" content="https://nextjs.org/video.mp4" />
<meta property="og:video:width" content="800" />
<meta property="og:video:height" content="600" />
<meta property="og:audio" content="https://nextjs.org/audio.mp3" />
<meta property="og:type" content="website" />
```

25 changes: 17 additions & 8 deletions docs/02-app/02-api-reference/05-next-config-js/assetPrefix.mdx
Original file line number Diff line number Diff line change
@@ -23,16 +23,25 @@ description: Learn how to use the assetPrefix config option to configure your CD
> suited for hosting your application on a sub-path like `/docs`.
> We do not suggest you use a custom Asset Prefix for this use case.
To set up a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network), you can set up an asset prefix and configure your CDN's origin to resolve to the domain that Next.js is hosted on.

Open `next.config.js` and add the `assetPrefix` config:
## Set up a CDN

```js filename="next.config.js"
const isProd = process.env.NODE_ENV === 'production'
To set up a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network), you can set up an asset prefix and configure your CDN's origin to resolve to the domain that Next.js is hosted on.

module.exports = {
// Use the CDN in production and localhost for development.
assetPrefix: isProd ? 'https://cdn.mydomain.com' : undefined,
Open `next.config.mjs` and add the `assetPrefix` config based on the [phase](/docs/app/api-reference/next-config-js#async-configuration):

```js filename="next.config.mjs"
// @ts-check
import { PHASE_DEVELOPMENT_SERVER } from 'next/constants'

export default (phase) => {
const isDev = phase === PHASE_DEVELOPMENT_SERVER
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
assetPrefix: isDev ? undefined : 'https://cdn.mydomain.com',
}
return nextConfig
}
```

2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -16,5 +16,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "14.2.10"
"version": "14.2.11"
}
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.2.10",
"version": "14.2.11",
"keywords": [
"react",
"next",
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.2.10",
"version": "14.2.11",
"description": "ESLint configuration used by Next.js.",
"main": "index.js",
"license": "MIT",
@@ -10,7 +10,7 @@
},
"homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config",
"dependencies": {
"@next/eslint-plugin-next": "14.2.10",
"@next/eslint-plugin-next": "14.2.11",
"@rushstack/eslint-patch": "^1.3.3",
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
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.2.10",
"version": "14.2.11",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"license": "MIT",
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.2.10",
"version": "14.2.11",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
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.2.10",
"version": "14.2.11",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
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.2.10",
"version": "14.2.11",
"license": "MIT",
"repository": {
"type": "git",
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.2.10",
"version": "14.2.11",
"keywords": [
"react",
"next",
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.2.10",
"version": "14.2.11",
"main": "index.js",
"license": "MIT",
"repository": {
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.2.10",
"version": "14.2.11",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
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.2.10",
"version": "14.2.11",
"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",
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.2.10",
"version": "14.2.11",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
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.2.10",
"version": "14.2.11",
"private": true,
"scripts": {
"clean": "node ../../scripts/rm.mjs native",
12 changes: 6 additions & 6 deletions packages/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next",
"version": "14.2.10",
"version": "14.2.11",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
@@ -92,7 +92,7 @@
]
},
"dependencies": {
"@next/env": "14.2.10",
"@next/env": "14.2.11",
"@swc/helpers": "0.5.5",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
@@ -149,10 +149,10 @@
"@jest/types": "29.5.0",
"@mswjs/interceptors": "0.23.0",
"@napi-rs/triples": "1.2.0",
"@next/polyfill-module": "14.2.10",
"@next/polyfill-nomodule": "14.2.10",
"@next/react-refresh-utils": "14.2.10",
"@next/swc": "14.2.10",
"@next/polyfill-module": "14.2.11",
"@next/polyfill-nomodule": "14.2.11",
"@next/react-refresh-utils": "14.2.11",
"@next/swc": "14.2.11",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "1.41.2",
"@taskr/clear": "1.1.0",
Original file line number Diff line number Diff line change
@@ -8,19 +8,19 @@ function getSocketProtocol(assetPrefix: string): string {
protocol = new URL(assetPrefix).protocol
} catch {}

return protocol === 'http:' ? 'ws' : 'wss'
return protocol === 'http:' ? 'ws:' : 'wss:'
}

export function getSocketUrl(assetPrefix: string | undefined): string {
const { hostname, port } = window.location
const protocol = getSocketProtocol(assetPrefix || '')
const prefix = normalizedAssetPrefix(assetPrefix)
const protocol = getSocketProtocol(assetPrefix || '')

// if original assetPrefix is a full URL with protocol
// we just update to use the correct `ws` protocol
if (assetPrefix?.replace(/^\/+/, '').includes('://')) {
return `${protocol}://${prefix}`
if (URL.canParse(prefix)) {
// since normalized asset prefix is ensured to be a URL format,
// we can safely replace the protocol
return prefix.replace(/^http/, 'ws')
}

return `${protocol}://${hostname}:${port}${prefix}`
const { hostname, port } = window.location
return `${protocol}//${hostname}${port ? `:${port}` : ''}${prefix}`
}
11 changes: 9 additions & 2 deletions packages/next/src/lib/metadata/generate/meta.tsx
Original file line number Diff line number Diff line change
@@ -53,10 +53,17 @@ function camelToSnake(camelCaseStr: string) {
})
}

const aliasPropPrefixes = new Set([
'og:image',
'twitter:image',
'og:video',
'og:audio',
])
function getMetaKey(prefix: string, key: string) {
// Use `twitter:image` and `og:image` instead of `twitter:image:url` and `og:image:url`
// to be more compatible as it's a more common format
if ((prefix === 'og:image' || prefix === 'twitter:image') && key === 'url') {
// to be more compatible as it's a more common format.
// `og:video` & `og:audio` do not have a `:url` suffix alias
if (aliasPropPrefixes.has(prefix) && key === 'url') {
return prefix
}
if (prefix.startsWith('og:') || prefix.startsWith('twitter:')) {
27 changes: 18 additions & 9 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
@@ -117,9 +117,12 @@ async function addRevalidationHeader(
requestStore: RequestStore
}
) {
await Promise.all(
Object.values(staticGenerationStore.pendingRevalidates || [])
)
await Promise.all([
staticGenerationStore.incrementalCache?.revalidateTag(
staticGenerationStore.revalidatedTags || []
),
...Object.values(staticGenerationStore.pendingRevalidates || {}),
])

// If a tag was revalidated, the client router needs to invalidate all the
// client router cache as they may be stale. And if a path was revalidated, the
@@ -481,9 +484,12 @@ export async function handleAction({

if (isFetchAction) {
res.statusCode = 500
await Promise.all(
Object.values(staticGenerationStore.pendingRevalidates || [])
)
await Promise.all([
staticGenerationStore.incrementalCache?.revalidateTag(
staticGenerationStore.revalidatedTags || []
),
...Object.values(staticGenerationStore.pendingRevalidates || {}),
])

const promise = Promise.reject(error)
try {
@@ -840,9 +846,12 @@ To configure the body size limit for Server Actions, see: https://nextjs.org/doc

if (isFetchAction) {
res.statusCode = 500
await Promise.all(
Object.values(staticGenerationStore.pendingRevalidates || [])
)
await Promise.all([
staticGenerationStore.incrementalCache?.revalidateTag(
staticGenerationStore.revalidatedTags || []
),
...Object.values(staticGenerationStore.pendingRevalidates || {}),
])
const promise = Promise.reject(err)
try {
// we need to await the promise to trigger the rejection early
9 changes: 6 additions & 3 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
@@ -1350,9 +1350,12 @@ async function renderToHTMLOrFlightImpl(

// If we have pending revalidates, wait until they are all resolved.
if (staticGenerationStore.pendingRevalidates) {
options.waitUntil = Promise.all(
Object.values(staticGenerationStore.pendingRevalidates)
)
options.waitUntil = Promise.all([
staticGenerationStore.incrementalCache?.revalidateTag(
staticGenerationStore.revalidatedTags || []
),
...Object.values(staticGenerationStore.pendingRevalidates || {}),
])
}

addImplicitTags(staticGenerationStore)
Original file line number Diff line number Diff line change
@@ -383,11 +383,14 @@ export class AppRouteRouteModule extends RouteModule<
context.renderOpts.fetchMetrics =
staticGenerationStore.fetchMetrics

context.renderOpts.waitUntil = Promise.all(
Object.values(
staticGenerationStore.pendingRevalidates || []
)
)
context.renderOpts.waitUntil = Promise.all([
staticGenerationStore.incrementalCache?.revalidateTag(
staticGenerationStore.revalidatedTags || []
),
...Object.values(
staticGenerationStore.pendingRevalidates || {}
),
])

addImplicitTags(staticGenerationStore)
;(context.renderOpts as any).fetchTags =
8 changes: 8 additions & 0 deletions packages/next/src/server/lib/router-server.ts
Original file line number Diff line number Diff line change
@@ -657,7 +657,15 @@ export async function initialize(opts: {
// assetPrefix overrides basePath for HMR path
if (assetPrefix) {
hmrPrefix = normalizedAssetPrefix(assetPrefix)

if (URL.canParse(hmrPrefix)) {
// remove trailing slash from pathname
// return empty string if pathname is '/'
// to avoid conflicts with '/_next' below
hmrPrefix = new URL(hmrPrefix).pathname.replace(/\/$/, '')
}
}

const isHMRRequest = req.url.startsWith(
ensureLeadingSlash(`${hmrPrefix}/_next/webpack-hmr`)
)
9 changes: 0 additions & 9 deletions packages/next/src/server/web/spec-extension/revalidate.ts
Original file line number Diff line number Diff line change
@@ -68,15 +68,6 @@ function revalidate(tag: string, expression: string) {
store.revalidatedTags.push(tag)
}

if (!store.pendingRevalidates) {
store.pendingRevalidates = {}
}
store.pendingRevalidates[tag] = store.incrementalCache
.revalidateTag?.(tag)
.catch((err) => {
console.error(`revalidate failed for ${tag}`, err)
})

// TODO: only revalidate if the path matches
store.pathWasRevalidated = true
}
Loading