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.1.1
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: f564deef86be32a6b25125ddb8172c7c27d3f19a
Choose a head ref
  • 10 commits
  • 43 files changed
  • 5 contributors

Commits on Mar 2, 2024

  1. use latest tag for publish

    ijjk committed Mar 2, 2024
    Copy the full SHA
    1ad6548 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4518c1e View commit details
  3. build: remove sentry from the externals list #61194

    ijjk committed Mar 2, 2024
    Copy the full SHA
    5aba536 View commit details

Commits on Mar 4, 2024

  1. make router restore action resilient to a missing tree (#62098)

    Following an anchor link to a hash param, and then attempting to use
    `history.pushState` or `history.replaceState`, would result in an MPA
    navigation to the targeted URL.
    
    In #61822, a guard was added to prevent calling `ACTION_RESTORE` with a
    missing tree, to match other call-sites where we do the same. This was
    to prevent the app from crashing in the case where app router internals
    weren't available in the history state. The original assumption was that
    this is a rare / unlikely edge case. However the above scenario is a
    very probable case where this can happen, and triggering an MPA
    navigation isn't ideal.
    
    This updates `ACTION_RESTORE` to be resilient to an undefined router
    state tree. When this happens, we'll still trigger the restore action to
    sync params, but use the existing flight router state.
    
    Closes NEXT-2502
    ztanner committed Mar 4, 2024
    Copy the full SHA
    a70ae40 View commit details
  2. ensure server action errors notify rejection handlers (#61588)

    ### What
    When attaching a rejection listener to a server action promise, in the case of network errors, the rejection handler would be skipped and it'd throw an error that crashes the application.
    
    ### Why
    When we refactored these reducers to no longer suspend, it caused the rejection handling logic we have to no longer make sense. In this case we're working with a native promise that won't have a `status` property, so we'd re-throw the error and not call `reject`. 
    
    ### How
    This removes the special status handling logic and makes the rejection handler always call `reject` with the error. This will either be handled by user code or let the error bubble to an error boundary. 
    
    I also cleaned up some mutable code that is no longer needed now that these reducers aren't replayed. 
    
    Closes NEXT-1715
    Closes NEXT-2323
    Fixes #58638
    ztanner committed Mar 4, 2024
    Copy the full SHA
    dbab205 View commit details
  3. fix: Add stricter check for "use server" exports (#62821)

    As mentioned in the new-added error messages, and the [linked
    resources](https://react.dev/reference/react/use-server#:~:text=Because%20the%20underlying%20network%20calls%20are%20always%20asynchronous%2C%20%27use%20server%27%20can%20only%20be%20used%20on%20async%20functions.):
    
    > Because the underlying network calls are always asynchronous, 'use
    server' can only be used on async functions.
    > https://react.dev/reference/react/use-server
    
    It's a requirement that only async functions are allowed to be exported
    and annotated with `'use server'`. Currently, we already have compiler
    check so this will already error:
    
    ```js
    'use server'
    
    export function foo () {} // missing async
    ```
    
    However, since exported values can be very dynamic the compiler can't
    catch all mistakes like that. We also have a runtime check for all
    exports in a `'use server'` function, but it only covers `typeof value
    === 'function'`.
    
    This PR adds a stricter check for "use server" annotated values to also
    make sure they're async functions (`value.constructor.name ===
    'AsyncFunction'`).
    
    That said, there are still cases like synchronously returning a promise
    to make a function "async", but it's still very different by definition.
    For example:
    
    ```js
    const f = async () => { throw 1; return 1 }
    const g = () => { throw 1; return Promise.resolve(1) }
    ```
    
    Where `g()` can be synchronously caught (`try { g() } catch {}`) but
    `f()` can't even if they have the same types. If we allow `g` to be a
    Server Action, this behavior is no longer always true but depending on
    where it's called (server or client).
    
    Closes #62727.
    shuding committed Mar 4, 2024
    Copy the full SHA
    714020f View commit details
  4. Fix redirect under suspense boundary with basePath (#62597)

    Fixes `redirect()` call under suspense boundary, redirect url should
    include the `basePath` in the url.
    
    `redirect()` under suspense boundaries will go through server html
    insertion, which is being used every time by suspense boundary resolved,
    new errors will triggered from SSR streaming. It was missing before.
    
    Fixes NEXT-2615
    Fixes #62407
    huozhi committed Mar 4, 2024
    Copy the full SHA
    400b57c View commit details
  5. Fix: generateSitemaps in production giving 404 (#62212)

    generateSitemaps function returns a 404 for /sitemap/[id].xml in
    production
    
    While finding the correct sitemap partition from the array, we check the
    param against the id. Which works in dev because id and param are both
    without trailing .xml. But it fails in production as param has a
    trailing .xml (/sitemap/[id] works in production because it falls back
    to dynamic loading and param and id are both without .xml)
    
    If we are in production environment, check the id with a trailing .xml
    because that's whats returned from generateStaticParams, an array of
    __metadata_id__ with trailing .xml
    
    Fixes #61969
    
    ---------
    
    Co-authored-by: Jiachi Liu <inbox@huozhi.im>
    abhinaypandey02 and huozhi committed Mar 4, 2024
    Copy the full SHA
    0a2d775 View commit details
  6. Fix sitemap generateSitemaps support for string id (#61088)

    Fixes the string id that broken when sitemap is optimized to static
    route.
    
    When sitemap is optimized to static route in production, the route
    argument is changed from `[[...__metadata_id__]]` to
    `[__metadata_id__]`, so the type of it is also changed from array to
    string that should reflect in the loader code.
    
    Fixes #60894
    Closes NEXT-2154
    huozhi committed Mar 4, 2024
    Copy the full SHA
    a85519e View commit details
  7. v14.1.2

    ijjk committed Mar 4, 2024
    Copy the full SHA
    f564dee View commit details
Showing with 444 additions and 95 deletions.
  1. +43 −0 errors/invalid-use-server-value.mdx
  2. +1 −1 lerna.json
  3. +1 −1 packages/create-next-app/package.json
  4. +2 −2 packages/eslint-config-next/package.json
  5. +1 −1 packages/eslint-plugin-next/package.json
  6. +1 −1 packages/font/package.json
  7. +1 −1 packages/next-bundle-analyzer/package.json
  8. +1 −1 packages/next-codemod/package.json
  9. +1 −1 packages/next-env/package.json
  10. +1 −1 packages/next-mdx/package.json
  11. +1 −1 packages/next-plugin-storybook/package.json
  12. +1 −1 packages/next-polyfill-module/package.json
  13. +1 −1 packages/next-polyfill-nomodule/package.json
  14. +1 −1 packages/next-swc/package.json
  15. +7 −7 packages/next/package.json
  16. +2 −1 packages/next/src/build/webpack-config.ts
  17. +16 −1 packages/next/src/build/webpack/loaders/next-flight-loader/action-validate.ts
  18. +16 −5 packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts
  19. +45 −0 packages/next/src/build/webpack/loaders/next-swc-loader.ts
  20. +0 −10 packages/next/src/build/webpack/plugins/terser-webpack-plugin/src/index.ts
  21. +5 −1 packages/next/src/client/components/app-router.tsx
  22. +10 −3 packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts
  23. +6 −20 packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts
  24. +5 −4 packages/next/src/client/components/router-reducer/router-reducer-types.ts
  25. +0 −2 packages/next/src/lib/server-external-packages.json
  26. +1 −0 packages/next/src/server/app-render/app-render.tsx
  27. +7 −1 packages/next/src/server/app-render/make-get-server-inserted-html.tsx
  28. +1 −1 packages/react-dev-overlay/package.json
  29. +1 −1 packages/react-refresh-utils/package.json
  30. +2 −2 packages/third-parties/package.json
  31. +8 −8 pnpm-lock.yaml
  32. +1 −3 scripts/publish-native.js
  33. +1 −1 scripts/publish-release.js
  34. +114 −2 test/e2e/app-dir/actions/app-action.test.ts
  35. +9 −0 test/e2e/app-dir/actions/app/catching-error/actions.ts
  36. +48 −0 test/e2e/app-dir/actions/app/catching-error/page.tsx
  37. +4 −0 test/e2e/app-dir/app-basepath/app/dynamic/[id]/loading.js
  38. +11 −0 test/e2e/app-dir/app-basepath/app/dynamic/[id]/page.js
  39. +11 −4 test/e2e/app-dir/app-basepath/index.test.ts
  40. +6 −1 test/e2e/app-dir/metadata-dynamic-routes/app/gsp/sitemap.ts
  41. +2 −2 test/e2e/app-dir/metadata-dynamic-routes/index.test.ts
  42. +6 −1 test/e2e/app-dir/shallow-routing/app/(shallow)/layout.tsx
  43. +42 −0 test/e2e/app-dir/shallow-routing/shallow-routing.test.ts
43 changes: 43 additions & 0 deletions errors/invalid-use-server-value.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: 'Invalid "use server" Value'
---

## Why This Error Occurred

This error occurs when a `"use server"` file exports a value that is not an async function. It might happen when you unintentionally export something like a configuration object, an arbitrary value, or missed the `async` keyword in the exported function declaration.

These functions are required to be defined as async, because `"use server"` marks them as [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations) and they can be invoked directly from the client through a network request.

Examples of incorrect code:

```js
'use server'

// ❌ This is incorrect: only async functions are allowed.
export const value = 1

// ❌ This is incorrect: missed the `async` keyword.
export function getServerData() {
return '...'
}
```

Correct code:

```js
'use server'

// ✅ This is correct: an async function is exported.
export async function getServerData() {
return '...'
}
```

## Possible Ways to Fix It

Check all exported values in the `"use server"` file (including `export *`) and make sure that they are all defined as async functions.

## Useful Links

- [Server Actions and Mutations - Next.js](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)
- ['use server' directive - React](https://react.dev/reference/react/use-server)
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.1.1"
"version": "14.1.2"
}
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",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"@next/eslint-plugin-next": "14.1.2",
"@rushstack/eslint-patch": "^1.3.3",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0",
"eslint-import-resolver-node": "^0.3.6",
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",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"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.1.1",
"version": "14.1.2",
"private": true,
"scripts": {
"clean": "node ../../scripts/rm.mjs native",
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",
"version": "14.1.2",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
@@ -92,7 +92,7 @@
]
},
"dependencies": {
"@next/env": "14.1.1",
"@next/env": "14.1.2",
"@swc/helpers": "0.5.2",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
@@ -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",
"@next/polyfill-nomodule": "14.1.1",
"@next/react-dev-overlay": "14.1.1",
"@next/react-refresh-utils": "14.1.1",
"@next/swc": "14.1.1",
"@next/polyfill-module": "14.1.2",
"@next/polyfill-nomodule": "14.1.2",
"@next/react-dev-overlay": "14.1.2",
"@next/react-refresh-utils": "14.1.2",
"@next/swc": "14.1.2",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "^1.35.1",
"@taskr/clear": "1.1.0",
3 changes: 2 additions & 1 deletion packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ if (parseInt(React.version) < 18) {
throw new Error('Next.js requires react >= 18.2.0 to be installed.')
}

const babelIncludeRegexes: RegExp[] = [
export const babelIncludeRegexes: RegExp[] = [
/next[\\/]dist[\\/](esm[\\/])?shared[\\/]lib/,
/next[\\/]dist[\\/](esm[\\/])?client/,
/next[\\/]dist[\\/](esm[\\/])?pages/,
@@ -455,6 +455,7 @@ export default async function getBaseWebpackConfig(
jsConfig,
supportedBrowsers,
swcCacheDir: path.join(dir, config?.distDir ?? '.next', 'cache', 'swc'),
transpilePackages: config.transpilePackages,
...extraOptions,
} satisfies SWCLoaderOptions,
}
Original file line number Diff line number Diff line change
@@ -6,7 +6,22 @@ export function ensureServerEntryExports(actions: any[]) {
const action = actions[i]
if (typeof action !== 'function') {
throw new Error(
`A "use server" file can only export async functions, found ${typeof action}.`
`A "use server" file can only export async functions, found ${typeof action}.\nRead more: https://nextjs.org/docs/messages/invalid-use-server-value`
)
}

if (action.constructor.name !== 'AsyncFunction') {
const actionName: string = action.name || ''

// Note: if it's from library code with `async` being transpiled to returning a promise,
// it would be annoying. But users can still wrap it in an async function to work around it.
throw new Error(
`A "use server" file can only export async functions.${
// If the function has a name, we'll make the error message more specific.
actionName
? ` Found "${actionName}" that is not an async function.`
: ''
}\nRead more: https://nextjs.org/docs/messages/invalid-use-server-value`
)
}
}
Original file line number Diff line number Diff line change
@@ -122,8 +122,8 @@ const generateImageMetadata = imageModule.generateImageMetadata
${errorOnBadHandler(resourcePath)}
export async function GET(_, ctx) {
const { __metadata_id__ = [], ...params } = ctx.params || {}
const targetId = __metadata_id__[0]
const { __metadata_id__, ...params } = ctx.params || {}
const targetId = __metadata_id__?.[0]
let id = undefined
const imageMetadata = generateImageMetadata ? await generateImageMetadata({ params }) : null
@@ -191,9 +191,16 @@ ${errorOnBadHandler(resourcePath)}
${'' /* re-export the userland route configs */}
export * from ${JSON.stringify(resourcePath)}
export async function GET(_, ctx) {
const { __metadata_id__ = [], ...params } = ctx.params || {}
const targetId = __metadata_id__[0]
const { __metadata_id__, ...params } = ctx.params || {}
${
'' /* sitemap will be optimized to [__metadata_id__] from [[..._metadata_id__]] in production */
}
const targetId = process.env.NODE_ENV !== 'production'
? __metadata_id__?.[0]
: __metadata_id__
let id = undefined
const sitemaps = generateSitemaps ? await generateSitemaps() : null
@@ -204,7 +211,11 @@ export async function GET(_, ctx) {
throw new Error('id property is required for every item returned from generateSitemaps')
}
}
return item.id.toString() === targetId
let itemID = item.id.toString()
if(process.env.NODE_ENV === 'production') {
itemID += '.xml'
}
return itemID === targetId
})?.id
if (id == null) {
return new NextResponse('Not Found', {
45 changes: 45 additions & 0 deletions packages/next/src/build/webpack/loaders/next-swc-loader.ts
Original file line number Diff line number Diff line change
@@ -31,6 +31,22 @@ import type { WebpackLayerName } from '../../../lib/constants'
import { isWasm, transform } from '../../swc'
import { getLoaderSWCOptions } from '../../swc/options'
import path, { isAbsolute } from 'path'
import { babelIncludeRegexes } from '../../webpack-config'
import { isResourceInPackages } from '../../handle-externals'

const maybeExclude = (
excludePath: string,
transpilePackages: string[]
): boolean => {
if (babelIncludeRegexes.some((r) => r.test(excludePath))) {
return false
}

const shouldBeBundled = isResourceInPackages(excludePath, transpilePackages)
if (shouldBeBundled) return false

return excludePath.includes('node_modules')
}

export interface SWCLoaderOptions {
rootDir: string
@@ -46,8 +62,14 @@ export interface SWCLoaderOptions {
serverComponents?: boolean
bundleLayer?: WebpackLayerName
esm?: boolean
transpilePackages?: string[]
}

// these are exact code conditions checked
// for to force transpiling a `node_module`
const FORCE_TRANSPILE_CONDITIONS =
/(next\/font|next\/dynamic|use server|use client)/

async function loaderTransform(
this: any,
parentTrace: any,
@@ -58,6 +80,20 @@ async function loaderTransform(
const filename = this.resourcePath

let loaderOptions: SWCLoaderOptions = this.getOptions() || {}
const shouldMaybeExclude = maybeExclude(
filename,
loaderOptions.transpilePackages || []
)

if (shouldMaybeExclude) {
if (!source) {
throw new Error(`Invariant might be excluded but missing source`)
}

if (!FORCE_TRANSPILE_CONDITIONS.test(source)) {
return [source, inputSourceMap]
}
}

const {
isServer,
@@ -150,8 +186,17 @@ const EXCLUDED_PATHS =

export function pitch(this: any) {
const callback = this.async()
let loaderOptions: SWCLoaderOptions = this.getOptions() || {}

const shouldMaybeExclude = maybeExclude(
this.resourcePath,
loaderOptions.transpilePackages || []
)

;(async () => {
if (
// if it might be excluded/no-op we can't use pitch loader
!shouldMaybeExclude &&
// TODO: investigate swc file reading in PnP mode?
!process.versions.pnp &&
!EXCLUDED_PATHS.test(this.resourcePath) &&
Original file line number Diff line number Diff line change
@@ -98,16 +98,6 @@ export class TerserPlugin {
return false
}

// don't minify _middleware as it can break in some cases
// and doesn't provide too much of a benefit as it's server-side
if (
name.match(
/(edge-runtime-webpack\.js|edge-chunks|middleware\.js$)/
)
) {
return false
}

const { info } = res

// Skip double minimize assets from child compilation
6 changes: 5 additions & 1 deletion packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
@@ -471,11 +471,14 @@ function Router({
url: string | URL | null | undefined
) => {
const href = window.location.href
const tree: FlightRouterState | undefined =
window.history.state?.__PRIVATE_NEXTJS_INTERNALS_TREE

startTransition(() => {
dispatch({
type: ACTION_RESTORE,
url: new URL(url ?? href, href),
tree: window.history.state.__PRIVATE_NEXTJS_INTERNALS_TREE,
tree,
})
})
}
@@ -494,6 +497,7 @@ function Router({
if (data?.__NA || data?._N) {
return originalPushState(data, _unused, url)
}

data = copyNextJsInternalHistoryState(data)

if (url) {
Loading