Repository: vercel/next.js
Pull request: Allow headers, rewrites and redirects to be defined as sync functions

### What and why?

Typings in `NextConfig` make us specify `headers`, `rewrites` and `redirects` as $${\color{red}async}$$ functions. Doing so is needed even if the result is known straight away, without having to call async IO.

This tight requirement requirement creates a small but recurring friction point in new Next.js projects. Even though passing a $${\color{blue}sync}$$ function is technically valid, it is necessary to prefix it with `async` keyword to make `tsc` happy.

I use [`@typescript-eslint/require-await`](https://typescript-eslint.io/rules/require-await/) which is included in [`@typescript-eslint/recommended-type-checked`](https://typescript-eslint.io/users/configs/#recommended-type-checked) preset. Because sync functions are not allowed in `headers`, `rewrites` and `redirects`, I have to add `async` and then `// eslint-disable-next-line` above it:

```ts
const nextConfig: NextConfig = {
  // ... rest of the config ...
  
  // eslint-disable-next-line @typescript-eslint/require-await -- External API requires async
  redirects: async () => {
    return [
      {
        source: "/example",
        destination: "/",
        permanent: true,
      },
    ];
  },
};
```

### How?

Just replacing `foo?: () Promise<Foo>` with `foo?: () Promise<Foo> | Foo` does the job. With Next 16.0.0 on the horizon, it looks like the right opportunity to tweak the typings. It's a non-breaking change for everyone except folks who have to silence `@typescript-eslint/require-await`.

### Context

Some config options already use `Promise<Foo> | Foo`:

https://github.com/vercel/next.js/blob/7396d7eb5907a2e9078a01a38a350819a8309b83/packages/next/src/server/config-shared.ts#L881-L890

https://github.com/vercel/next.js/blob/7396d7eb5907a2e9078a01a38a350819a8309b83/packages/next/src/server/config-shared.ts#L1014-L1017

Judging from [`git blame` for `config-shared.ts`](https://github.com/vercel/next.js/blame/7396d7eb5907a2e9078a01a38a350819a8309b83/packages/next/src/server/config-shared.ts), this inconsistency appears to be a historic artifact rather than something designed.

Changed files:
filename: packages/next/src/server/config-shared.ts
patch: @@ -920,28 +920,35 @@ export interface NextConfig extends Record<string, any> {
    *
    * @see [Headers configuration documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/headers)
    */
-  headers?: () => Promise<Header[]>
+  headers?: () => Promise<Header[]> | Header[]
 
   /**
    * Rewrites allow you to map an incoming request path to a different destination path.
    *
    * @see [Rewrites configuration documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites)
    */
-  rewrites?: () => Promise<
+  rewrites?: () =>
+    | Promise<
+        | Rewrite[]
+        | {
+            beforeFiles?: Rewrite[]
+            afterFiles?: Rewrite[]
+            fallback?: Rewrite[]
+          }
+      >
     | Rewrite[]
     | {
         beforeFiles?: Rewrite[]
         afterFiles?: Rewrite[]
         fallback?: Rewrite[]
       }
-  >
 
   /**
    * Redirects allow you to redirect an incoming request path to a different destination path.
    *
    * @see [Redirects configuration documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/redirects)
    */
-  redirects?: () => Promise<Redirect[]>
+  redirects?: () => Promise<Redirect[]> | Redirect[]
 
   /**
    * @see [Moment.js locales excluded by default](https://nextjs.org/docs/upgrading#momentjs-locales-excluded-by-default)