## What's in there?
The Edge runtime [does not support Node.js modules](https://edge-runtime.vercel.app/features/available-apis#unsupported-apis).
When building Next.js application, we currently fail the build when detecting node.js module imported from middleware.
This is an blocker for using code that is conditionally loading node.js modules (based on platform/env detection), as @cramforce reported.
This PR implements a new strategy where:
- we can build such middleware/Edge API route code **with a warning**
- we fail at run time, with graceful errors in dev (console & react-dev-overlay error)
- we fail at run time, with console errors in production
## How to test?
All cases are covered with integration tests.
To try them live, create a simple app with a page, a `middleware.js` file and a `pages/api/route.js`file.
Here are iconic examples:
### node.js modules
```js
// middleware.js
import { NextResponse } from 'next/server'
// static
import { basename } from 'path'
export default async function middleware() {
// dynamic
const { basename } = await import('path')
basename()
return NextResponse.next()
}
export const config = { matcher: '/' }
```
```js
// pags/api/route.js
// static
import { isAbsolute } from 'path'
export default async function handle() {
// dynamic
const { isAbsolute } = await import('path')
return Response.json({ useNodeModule: isAbsolute('/test') })
}
export const config = { runtime: 'experimental-edge' }
```
Desired error (+ source code highlight in dev):
> The edge runtime does not support Node.js 'path' module
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
Desired warning at build time:
> A Node.js module is loaded ('path' at line 2) which is not supported in the Edge Runtime.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
- [x] in dev middleware, static, shows desired error on stderr
- [x] in dev route, static, shows desired error on stderr
- [x] in dev middleware, dynamic, shows desired error on stderr
- [x] in dev route, dynamic, shows desired error on stderr
- [x] in dev middleware, static, shows desired error on react error overlay
- [x] in dev route, static, shows desired error on react error overlay
- [x] in dev middleware, dynamic, shows desired error on react error overlay
- [x] in dev route, dynamic, shows desired error on react error overlay
- [x] builds middleware successfully, shows build warning, shows desired error on stderr on call
- [x] builds route successfully, shows build warning, shows desired error on stderr on call
### 3rd party modules not found
```js
// middleware.js
import { NextResponse } from 'next/server'
// static
import Unknown from 'unknown'
export default async function middleware() {
// dynamic
const Unknown = await import('unknown')
new Unknown()
return NextResponse.next()
}
```
export const config = { matcher: '/' }
```
```js
// pags/api/route.js
// static
import Unknown from 'unknown'
export default async function handle() {
// dynamic
const Unknown = await import('unknown')
return Response.json({ use3rdPartyModule: Unknown() })
}
export const config = { runtime: 'experimental-edge' }
```
Desired error (+ source code highlight in dev):
> Module not found: Can't resolve 'does-not-exist'
Learn More: https://nextjs.org/docs/messages/module-not-found
- [x] in dev middleware, static, shows desired error on stderr
- [x] in dev route, static, shows desired error on stderr
- [x] in dev middleware, dynamic, shows desired error on stderr
- [x] in dev route, dynamic, shows desired error on stderr
- [x] in dev middleware, static, shows desired error on react error overlay
- [x] in dev route, static, shows desired error on react error overlay
- [x] in dev middleware, dynamic, shows desired error on react error overlay
- [x] in dev route, dynamic, shows desired error on react error overlay
- [x] fails to build middleware, with desired error on stderr
- [x] fails to build route, with desired error on stderr
### unused node.js modules
```js
// middleware.js
import { NextResponse } from 'next/server'
export default async function middleware() {
if (process.exit) {
const { basename } = await import('path')
basename()
}
return NextResponse.next()
}
```
```js
// pags/api/route.js
export default async function handle() {
if (process.exit) {
const { basename } = await import('path')
basename()
}
return Response.json({ useNodeModule: false })
}
export const config = { runtime: 'experimental-edge' }
```
Desired warning at build time:
> A Node.js module is loaded ('path' at line 2) which is not supported in the Edge Runtime.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
- [x] invoke middleware in dev with no error
- [x] invoke route in dev with no error
- [x] builds successfully, shows build warning, invoke middleware with no error
- [x] builds successfully, shows build warning, invoke api-route with no error
## Notes to reviewers
The strategy to implement this feature is to leverages webpack [externals](https://webpack.js.org/configuration/externals/#externals) and run a global `__unsupported_module()` function when using a node.js module from edge function's code.
For the record, I tried using [webpack resolve.fallback](https://webpack.js.org/configuration/resolve/#resolvefallback) and [Webpack.IgnorePlugin](https://webpack.js.org/plugins/ignore-plugin/) but they do not allow throwing proper errors at runtime that would contain the loaded module name for reporting.
`__unsupported_module()` is defined in `EdgeRuntime`, and returns a proxy that's throw on use (whether it's property access, function call, new operator... synchronous & promise-based styles).
However there's an issue with error reporting: webpack does not includes the import lines in the generated sourcemaps, preventing from displaying useful errors.
I extended our middleware-plugin to supplement the sourcemaps (when analyzing edge function code, it saves which module is imported from which file, together with line/column/source)
The react-dev-overlay was adapted to look for this additional information when the caught error relates to modules, instead of looking at sourcemaps.
I removed the previous mechanism (built by @nkzawa ) which caught webpack errors at built time to change the displayed error message (files `next/build/index.js`, `next/build/utils.ts` and `wellknown-errors-plugin`)
* Refactor `runDependingOnPageType`
* Throw specific error when the compiled middleware cannot be found
* Do not render `MiddlewareNotFoundError` on dev
* Allow to invalidate compilers by type
* Show compile errors when middleware fails to build
* Add tests
x-ref: #31506
This PR migrates existing SSR on edge from middleware to edge functions implmentation. So that we can get rid of limitation of middleware and resolve the conflicts between middleware and edge SSR routes.
* Adding edge functions matching route in middleware catch all route,keep the order as `middleware catch all` -> redirects/rewrites -> `edge catch all` -> others
* Dropping middleware related code for edge SSR: removing client info and preflight request handling
### What's in there?
This is a followup of https://github.com/vercel/next.js/pull/37695.
For the dev server to clean stacktraces, we're decorating errors caught during code evaluation (`getServerSideProps` or middleware).
However, when these errors are asynchronously raised, we can't decorate them before processing them, leading to this fallback logic:
bf7bf8217f/packages/next/server/dev/next-dev-server.ts (L775-L779)
Thanks to latest improvement of the edge-runtime in 1.1.0-beta.4, we can now catch unhandled rejection and uncaught exception, and decorate them.
### How to test?
Please reuse the existing tests who already covered these cases:
`pnpm testheadless --testPathPattern middleware-dev-errors`
Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
This PR fixes an issue where we have a middleware that rewrites every single request to the same origin while having `i18n` configured. It would be something like:
```typescript
import { NextResponse } from 'next/server'
export function middleware(req) {
return NextResponse.rewrite(req.nextUrl)
}
```
In this case we are going to be adding always the `locale` at the beginning of the destination since it is a rewrite. This causes static assets to not match and the whole application to break. I believe this is a potential footgun so in this PR we are addressing the issue by removing the locale from pathname for those cases where we check against the filesystem (e.g. public folder).
To achieve this change, this PR introduces some preparation changes and then a refactor of the logic in the server router. After this refactor we are going to be relying on properties that can be defined in the `Route` to decide wether or not we should remove the `basePath`, `locale`, etc instead of checking which _type_ of route it is that we are matching.
Overall this simplifies quite a lot the server router. The way we are testing the mentioned issue is by adding a default rewrite in the rewrite tests middleware.
* Improve stack traces in dev mode
* Refactor `react-dev-overlay` to support the Edge Compiler
* Serialize errors including the compiler `source`
* Adopt the new `react-dev-overlay` displaying it for middleware errors
* Improve tests
* fix rsc cases
* update test
* use check for dev test
* handle different error from node version
Co-authored-by: feugy <damien@vercel.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
* Ensure custom middleware matcher is used correctly in client manifest
* lint-fix
* patch e2e case
* fix rsc case
* update test
* add missing normalize
* Re-introduce Edge API Endpoints
This reverts commit 210fa39961, and
re-introduces Edge API endpoints as a possible runtime selection in API
endpoints.
This is done by exporting a `config` object:
```ts
export config = { runtime: 'edge' }
```
Note: `'edge'` will probably change into `'experimental-edge'` to show
that this is experimental and the API might change in the future.
* Support `experimental-edge`, but allow `edge` too
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
* Update to process redirects/rewrites for _next/data
* correct matched-path resolving with middleware
* Add next-data header
* migrate middleware tests
* lint-fix
* update error case
* update test case
* Handle additional resolving cases and add more tests
* update test from merge
* fix test
* rm .only
* apply changes from review
* ensure _next/data resolving does not apply without middleware
* Refactor data fetching to support getting headers
* Relax `getNextPathnameInfo` type
* Add test for middleware internal redirects
* Export `ParsedRelativeUrl` type
* Refactor `getMiddlewareEffects`
* Move rewrite i18n test to middleware rewrite tests
* Fix bug parsing pathname info
* Normalize data requests to page requests for middleware
* Ensure there is a header `x-nextjs-matched-path` for middleware rewrites on data requests
* Extract `getDataHref` to a function
* Stop using `getDataHref` for flight
* Always set the query in `dataHref` independently of if it is SSG
* Add test for recursive rewrites
* Refactor dynamicPath validation to `matchHrefAndAsPath`
* Add `dataHref` to `FetchDataOutput`
* Extract `matchesMiddleware` function
* Add `hasMiddleware` option to `fetchNextData`
* Move preflight test
* Remove preflight test
* Add middleware prefetch tests
* Remove preflight
* Attempt to reduce bundle size
Include `withMiddlewareEffects` and `matchHrefAndAsPath` into `router`
Bring `getDataHref` back to `page-loader`
Bring `resolveDynamicRoute` back to `router`
* Reduce arg duplication for `withMiddlewareEffects`
* Remove some async/await and spreads to reduce bundle size
* Upgrade `edge-runtime` & clone `Request` on redirects to mutate headers
* Add some rewrite tests
Co-authored-by: Kiko Beats <josefrancisco.verdu@gmail.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
## Bug
fixes#37106
Please note that, as for `pages/` the `src/middleware` file is ignored when `/middleware` is present.
## How to test
1. Rebuild next.js `pnpm build`
2. Run dedicated tests: `pnpm testheadless --testPathPattern middleware-src/`
This PR will allow Middleware to set its matcher through `export const config = { matching: ... }`
## Related
* This PR is rebased off #37121
## Feature
- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`
## Feature
This PR introduces the ability to provide `runtime: "edge"` in API endpoints, the same as the experimental RSC runtime configurations.
- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`
* Do not exclude internal _next request in middleware
* Allow for `NextURL` to parse prefetch requests
* Add test for middleware data prefetch
* Refactor `hasBasePath` and `replaceBasePath`
* Refactor `removeTrailingSlash`
* Refactor parsed next url to use `getNextPathnameInfo`
* Allow to configure `NextURL`
* Ensure middleware rewrites with always with a locale
Co-authored-by: JJ Kasper <jj@jjsweb.site>
## Bug
- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
The PR fixes#37122, an issue introduced by #37105.
`next.config.js` might/will include functions (custom `webpack`, `generateBuildId`, `exportPathsMap`, etc.) that are not able to pass from the main thread to a worker. The PR fixes the issue by only passing primitive args to the worker.
This PR deprecates declaring a middleware under `pages` in favour of the project root naming it after `middleware` instead of `_middleware`. This is in the context of having a simpler execution model for middleware and also ships some refactor work. There is a ton of a code to be simplified after this deprecation but I think it is best to do it progressively.
With this PR, when in development, we will **fail** whenever we find a nested middleware but we do **not** include it in the compiler so if the project is using it, it will no longer work. For production we will **fail** too so it will not be possible to build and deploy a deprecated middleware. The error points to a page that should also be reviewed as part of **documentation**.
Aside from the deprecation, this migrates all middleware tests to work with a single middleware. It also splits tests into multiple folders to make them easier to isolate and work with. Finally it ships some small code refactor and simplifications.
Stack trace disappears when error is converted to string.
I changed the types in `log.ts` to match `console.log`/`console.error`/`console.warn`.
fixes#31591
This PR brings some significant refactoring in preparation for upcoming middleware changes. Each commit can be reviewed independently, here is a summary of what each one does and the reasoning behind it:
- [Move pagesDir to next-dev-server](f2fe154c00) simply moves the `pagesDir` property to the dev server which is the only place where it is needed. Having it for every server is misleading.
- [Move (de)normalize page path utils to a file page-path-utils.ts](27cedf0871) Moves the functions to normalize and denormalize page paths to a single file that is intended to hold every utility function that transforms page paths. Since those are complementary it makes sense to have them together. I also added explanatory comments on why they are not idempotent and examples for input -> output that I find very useful.
- [Extract removePagePathTail](6b121332aa) This extracts a function to remove the tail on a page path (absolute or relative). I'm sure there will be other contexts where we can use it.
- [Extract getPagePaths and refactor findPageFile](cf2c7b842e) This extracts a function `getPagePaths` that is used to generate an array of paths to inspect when looking for a page file from `findPageFile`. Then it refactors such function to use it parallelizing lookups. This will allow us to print every path we look at when looking for a file which can be useful for debugging. It also adds a `flatten` helper.
- [Refactor onDemandEntryHandler](4be685c37e) I've found this one quite difficult to understand so it is refactored to use some of the previously mentioned functions and make it easier to read.
- [Extract absolutePagePath util](3bc0783474) Extracts yet another util from the `next-dev-server` that transforms an absolute path into a page name. Of course it adds comments, parameters and examples.
- [Refactor MiddlewarePlugin](c595a2cc62) This is the most significant change. The logic here was very hard to understand so it is totally redistributed with comments. This also removes a global variable `ssrEntries` that was deprecated in favour of module metadata added to Webpack from loaders keeping less dependencies. It also adds types and makes a clear distinction between phases where we statically analyze the code, find metadata and generate the manifest file cc @shuding @huozhi
EDIT:
- [Split page path utils](158fb002d0) After seeing one of the utils was being used by the client while it was defined originally in the server, with this PR we are splitting the util into multiple files and moving it to `shared/lib` in order to make explicit that those can be also imported from client.
x-ref: #33149
RFCs:
- #30996
- #31506
## Feature
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
* Read the proper page file from either pages directory or from node_modules (inernal pages like _app, _document)
* Only reading page runtime when `reactRoot` is enabled, reduce time for react 17 apps
* fix the dynamic routing of middleware
* add middleware to dynamicRoutes of routes-manifest
* remove unused import
* fix middleware routing with static paths
* update manifest version test
* prevent to match with api route using regex
* use iterator instead of generator
* do not use Iterator
* fix type
* fix type
* remove unused import
* apply the fix for support colons
Co-authored-by: JJ Kasper <jj@jjsweb.site>
Moves two utility functions from `server/router.ts` into their own file. This avoids the middleware pulling in the full Next.js router into its bundle.
There are probably more opportunities like this, but this is a good start. Middleware should likely be bundled by a non-chunking optimizing compiler.
## Summary
This PR adds a new `worker` strategy to the `<Script />` component that automatically relocates and executes the script in a web worker.
```jsx
<Script
strategy="worker"
...
/>
```
[Partytown](https://partytown.builder.io/) is used under the hood to provide this functionality.
## Behavior
- This will land as an experimental feature and will only work behind an opt-in flag in `next.config.js`:
```js
experimental: {
nextScriptWorkers: true
}
```
- This setup use a similar approach to how ESLint and Typescript is used in Next.js by showing an error to the user to install the dependency locally themselves if they've enabled the experimental `nextScriptWorkers` flag.
<img width="1068" alt="Screen Shot 2022-03-03 at 2 33 13 PM" src="https://user-images.githubusercontent.com/12476932/156639227-42af5353-a2a6-4126-936e-269112809651.png">
- For Partytown to work, a number of static files must be served directly from the site (see [docs](https://partytown.builder.io/copy-library-files)). In this PR, these files are automatically copied to a `~partytown` directory in `.next/static` during `next build` and `next dev` if the `nextScriptWorkers` flag is set to true.
## Checklist
- [X] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [X] Related issues linked using `fixes #number`
- [X] Integration tests added
- [X] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
This PR fixes#31517.
This PR removes the `` const documentPage = `_document${globalRuntime ? '-concurrent' : ''}` `` condition from the config resolution phrase, and only use the built-in one. And later when rendering, we can conditionally convert the default class component into a function component. This change is necessary for the switchable runtime feature (#31506).
## Bug
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
## Feature
- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`
Partially implements #31317 and #31506. There're also some trade-offs made with this PR: since we can't know if a certain runtime will be used or not beforehand, we have to start both runtime compilers (Node.js and Edge) and then generate entrypoints correspondingly.
Note that with this PR, the global runtime is still required to use the per-page runtime.
## Bug
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
## Feature
- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`
fixes https://github.com/vercel/next.js/issues/34199
Also fixes response code to return `500` when showing error page of preflight request failure on dev mode
## Bug
- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
### Changes
* node server and webpack should share the same logic: auto detect react 18 and enable `reactRoot`
* fallback `_error` should use functional document if concurrent rendering is enabled
### Test
* Remove the hard code `reactRoot: true` in test suite
* Test some react-18 test suite with nodejs runtime
Related to #34185, this PR reduces the size of chunk that contains web-server.ts from 1.14mb to 210.8kb, by splitting base-http and api-utils into different environments.
Only affected thing is we can't have SSG preview mode for the web runtime via `getStaticProps`.
## Bug
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
## Feature
- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`