diff --git a/demos/middleware/middleware.ts b/demos/middleware/middleware.ts
index 4ef5240597..d934dc5fa5 100644
--- a/demos/middleware/middleware.ts
+++ b/demos/middleware/middleware.ts
@@ -59,6 +59,12 @@ export async function middleware(req: NextRequest) {
return response
}
+ if(pathname.startsWith('/matcher-cookie')) {
+ response = NextResponse.next()
+ response.cookies.set('missingCookie', 'true')
+ return response
+ }
+
if (pathname.startsWith('/conditional')) {
response = NextResponse.next()
response.headers.set('x-modified-edge', 'true')
@@ -66,6 +72,12 @@ export async function middleware(req: NextRequest) {
return response
}
+ if (pathname.startsWith('/missing')) {
+ response = NextResponse.next()
+ response.headers.set('x-cookie-missing', 'true')
+ return response
+ }
+
if (pathname.startsWith('/shows')) {
if (pathname.startsWith('/shows/222')) {
response = NextResponse.next()
@@ -119,6 +131,7 @@ export const config = {
'/headers',
{ source: '/static' },
{ source: '/cookies' },
+ { source: '/matcher-cookie'},
{ source: '/shows/((?!99|88).*)' },
{
source: '/conditional',
@@ -130,5 +143,14 @@ export const config = {
},
],
},
+ {
+ source: '/missing',
+ missing: [
+ {
+ type: 'cookie',
+ key: 'missingCookie',
+ }
+ ],
+ },
],
}
diff --git a/demos/middleware/pages/matcher-cookie.js b/demos/middleware/pages/matcher-cookie.js
new file mode 100644
index 0000000000..24f0cb6626
--- /dev/null
+++ b/demos/middleware/pages/matcher-cookie.js
@@ -0,0 +1,9 @@
+const MatcherCookie = () => {
+ return (
+
+
The cookie "missingCookie" should be set to true
+
+ )
+}
+
+export default MatcherCookie
diff --git a/demos/middleware/pages/missing.js b/demos/middleware/pages/missing.js
new file mode 100644
index 0000000000..195e0a2978
--- /dev/null
+++ b/demos/middleware/pages/missing.js
@@ -0,0 +1,13 @@
+import * as React from 'react'
+import Link from 'next/link'
+
+const Missing = () => {
+ return (
+
+
Will Check if 'missingCookie' is missing and display headers
+
To test go to cookies page and come back
+
+ )
+}
+
+export default Missing
diff --git a/package-lock.json b/package-lock.json
index 1f7264701f..cbfd38dd1f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24652,6 +24652,201 @@
"engines": {
"node": ">=10"
}
+ },
+ "node_modules/@next/swc-android-arm-eabi": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.7.tgz",
+ "integrity": "sha512-QTEamOK/LCwBf05GZ261rULMbZEpE3TYdjHlXfznV+nXwTztzkBNFXwP67gv2wW44BROzgi/vrR9H8oP+J5jxg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-android-arm64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.7.tgz",
+ "integrity": "sha512-wcy2H0Tl9ME8vKy2GnJZ7Mybwys+43F/Eh2Pvph7mSDpMbYBJ6iA0zeY62iYYXxlZhnAID3+h79FUqUEakkClw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.7.tgz",
+ "integrity": "sha512-F/mU7csN1/J2cqXJPMgTQ6MwAbc1pJ6sp6W+X0z5JEY4IFDzxKd3wRc3pCiNF7j8xW381JlNpWxhjCctnNmfaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.7.tgz",
+ "integrity": "sha512-636AuRQynCPnIPRVzcCk5B7OMq9XjaYam2T0HeWUCE6y7EqEO3kxiuZ4QmN81T7A6Ydb+JnivYrLelHXmgdj6A==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-freebsd-x64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.7.tgz",
+ "integrity": "sha512-92XAMzNgQazowZ9t7uZmHRA5VdBl/SwEdrf5UybdfRovsxB4r3+yJWEvFaqYpSEp0gwndbwLokJdpz7OwFdL3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm-gnueabihf": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.7.tgz",
+ "integrity": "sha512-3r1CWl5P6I5n5Yxip8EXv/Rfu2Cp6wVmIOpvmczyUR82j+bcMkwPAcUjNkG/vMCagS4xV7NElrcdGb39iFmfLg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.7.tgz",
+ "integrity": "sha512-RXo8tt6ppiwyS6hpDw3JdAjKcdVewsefxnxk9xOH4mRhMyq9V2lQx0e24X/dRiZqkx3jnWReR2WRrUlgN1UkSQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.7.tgz",
+ "integrity": "sha512-RWpnW+bmfXyxyY7iARbueYDGuIF+BEp3etLeYh/RUNHb9PhOHLDgJOG8haGSykud3a6CcyBI8hEjqOhoObaDpw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.7.tgz",
+ "integrity": "sha512-/ygUIiMMTYnbKlFs5Ba9J5k/tNxFWy8eI1bBF8UuMTvV8QJHl/aLDiA5dwsei2kk99/cu3eay62JnJXkSk3RSQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.7.tgz",
+ "integrity": "sha512-dLzr6AL77USJN0ejgx5AS8O8SbFlbYTzs0XwAWag4oQpUG2p3ARvxwQgYQ0Z+6EP0zIRZ/XfLkN/mhsyi3m4PA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.7.tgz",
+ "integrity": "sha512-+vFIVa82AwqFkpFClKT+n73fGxrhAZ2u1u3mDYEBdxO6c9U4Pj3S5tZFsGFK9kLT/bFvf/eeVOICSLCC7MSgJQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-ia32-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.7.tgz",
+ "integrity": "sha512-RNLXIhp+assD39dQY9oHhDxw+/qSJRARKhOFsHfOtf8yEfCHqcKkn3X/L+ih60ntaEqK294y1WkMk6ylotsxwA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.7.tgz",
+ "integrity": "sha512-kvdnlLcrnEq72ZP0lqe2Z5NqvB9N5uSCvtXJ0PhKvNncWWd0fEG9Ec9erXgwCmVlM2ytw41k9/uuQ+SVw4Pihw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
}
},
"dependencies": {
@@ -42964,6 +43159,84 @@
"compress-commons": "^4.1.0",
"readable-stream": "^3.6.0"
}
+ },
+ "@next/swc-android-arm-eabi": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.7.tgz",
+ "integrity": "sha512-QTEamOK/LCwBf05GZ261rULMbZEpE3TYdjHlXfznV+nXwTztzkBNFXwP67gv2wW44BROzgi/vrR9H8oP+J5jxg==",
+ "optional": true
+ },
+ "@next/swc-android-arm64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.7.tgz",
+ "integrity": "sha512-wcy2H0Tl9ME8vKy2GnJZ7Mybwys+43F/Eh2Pvph7mSDpMbYBJ6iA0zeY62iYYXxlZhnAID3+h79FUqUEakkClw==",
+ "optional": true
+ },
+ "@next/swc-darwin-arm64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.7.tgz",
+ "integrity": "sha512-F/mU7csN1/J2cqXJPMgTQ6MwAbc1pJ6sp6W+X0z5JEY4IFDzxKd3wRc3pCiNF7j8xW381JlNpWxhjCctnNmfaw==",
+ "optional": true
+ },
+ "@next/swc-darwin-x64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.7.tgz",
+ "integrity": "sha512-636AuRQynCPnIPRVzcCk5B7OMq9XjaYam2T0HeWUCE6y7EqEO3kxiuZ4QmN81T7A6Ydb+JnivYrLelHXmgdj6A==",
+ "optional": true
+ },
+ "@next/swc-freebsd-x64": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.7.tgz",
+ "integrity": "sha512-92XAMzNgQazowZ9t7uZmHRA5VdBl/SwEdrf5UybdfRovsxB4r3+yJWEvFaqYpSEp0gwndbwLokJdpz7OwFdL3Q==",
+ "optional": true
+ },
+ "@next/swc-linux-arm-gnueabihf": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.7.tgz",
+ "integrity": "sha512-3r1CWl5P6I5n5Yxip8EXv/Rfu2Cp6wVmIOpvmczyUR82j+bcMkwPAcUjNkG/vMCagS4xV7NElrcdGb39iFmfLg==",
+ "optional": true
+ },
+ "@next/swc-linux-arm64-gnu": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.7.tgz",
+ "integrity": "sha512-RXo8tt6ppiwyS6hpDw3JdAjKcdVewsefxnxk9xOH4mRhMyq9V2lQx0e24X/dRiZqkx3jnWReR2WRrUlgN1UkSQ==",
+ "optional": true
+ },
+ "@next/swc-linux-arm64-musl": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.7.tgz",
+ "integrity": "sha512-RWpnW+bmfXyxyY7iARbueYDGuIF+BEp3etLeYh/RUNHb9PhOHLDgJOG8haGSykud3a6CcyBI8hEjqOhoObaDpw==",
+ "optional": true
+ },
+ "@next/swc-linux-x64-gnu": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.7.tgz",
+ "integrity": "sha512-/ygUIiMMTYnbKlFs5Ba9J5k/tNxFWy8eI1bBF8UuMTvV8QJHl/aLDiA5dwsei2kk99/cu3eay62JnJXkSk3RSQ==",
+ "optional": true
+ },
+ "@next/swc-linux-x64-musl": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.7.tgz",
+ "integrity": "sha512-dLzr6AL77USJN0ejgx5AS8O8SbFlbYTzs0XwAWag4oQpUG2p3ARvxwQgYQ0Z+6EP0zIRZ/XfLkN/mhsyi3m4PA==",
+ "optional": true
+ },
+ "@next/swc-win32-arm64-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.7.tgz",
+ "integrity": "sha512-+vFIVa82AwqFkpFClKT+n73fGxrhAZ2u1u3mDYEBdxO6c9U4Pj3S5tZFsGFK9kLT/bFvf/eeVOICSLCC7MSgJQ==",
+ "optional": true
+ },
+ "@next/swc-win32-ia32-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.7.tgz",
+ "integrity": "sha512-RNLXIhp+assD39dQY9oHhDxw+/qSJRARKhOFsHfOtf8yEfCHqcKkn3X/L+ih60ntaEqK294y1WkMk6ylotsxwA==",
+ "optional": true
+ },
+ "@next/swc-win32-x64-msvc": {
+ "version": "13.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.7.tgz",
+ "integrity": "sha512-kvdnlLcrnEq72ZP0lqe2Z5NqvB9N5uSCvtXJ0PhKvNncWWd0fEG9Ec9erXgwCmVlM2ytw41k9/uuQ+SVw4Pihw==",
+ "optional": true
}
}
}
diff --git a/package.json b/package.json
index b20f02efef..bc7539e5e0 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"description": "Run Next.js seamlessly on Netlify",
"scripts": {
"build:demo": "cd demos/default && npm run build",
- "cy:open": "cypress open --config-file cypress/config/canary.json",
+ "cy:open": "cypress open --config-file cypress/config/all.json",
"dev:demo": "next dev demos/default",
"format": "run-s format:check-fix:*",
"format:ci": "run-s format:check:*",
diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts
index 65b8441c37..b02e0890ac 100644
--- a/packages/runtime/src/helpers/edge.ts
+++ b/packages/runtime/src/helpers/edge.ts
@@ -31,6 +31,7 @@ export interface MiddlewareMatcher {
regexp: string
locale?: false
has?: RouteHas[]
+ missing?: RouteHas[]
}
// This is the format after next@12.3.0
diff --git a/packages/runtime/src/templates/edge-shared/next-utils.ts b/packages/runtime/src/templates/edge-shared/next-utils.ts
index 5fdc34e7ce..e55c9a4d2d 100644
--- a/packages/runtime/src/templates/edge-shared/next-utils.ts
+++ b/packages/runtime/src/templates/edge-shared/next-utils.ts
@@ -42,6 +42,7 @@ export type Rewrite = {
basePath?: false
locale?: false
has?: RouteHas[]
+ missing?: RouteHas[]
regex: string
}
@@ -51,6 +52,7 @@ export type Header = {
locale?: false
headers: Array<{ key: string; value: string }>
has?: RouteHas[]
+ missing?: RouteHas[]
regex: string
}
export type Redirect = {
@@ -59,6 +61,7 @@ export type Redirect = {
basePath?: false
locale?: false
has?: RouteHas[]
+ missing?: RouteHas[]
statusCode?: number
permanent?: boolean
regex: string
@@ -138,11 +141,16 @@ export function parseUrl(url: string): ParsedUrl {
// prepare-destination.ts
// Changed to use WHATWG Fetch Request instead of IncomingMessage
-export function matchHas(req: Pick, has: RouteHas[], query: Params): false | Params {
+export function matchHas(
+ req: Pick,
+ query: Params,
+ has: RouteHas[] = [],
+ missing: RouteHas[] = [],
+): false | Params {
const params: Params = {}
const cookies = getCookies(req.headers)
const url = new URL(req.url)
- const allMatch = has.every((hasItem) => {
+ const hasMatch = (hasItem: RouteHas) => {
let value: undefined | string | null
let key = hasItem.key
@@ -189,7 +197,9 @@ export function matchHas(req: Pick, has: RouteHas[],
}
}
return false
- })
+ }
+
+ const allMatch = has.every((item) => hasMatch(item)) && !missing.some((item) => hasMatch(item))
if (allMatch) {
return params
@@ -371,6 +381,7 @@ export interface MiddlewareMatcher {
regexp: string
locale?: false
has?: RouteHas[]
+ missing?: RouteHas[]
}
export function getMiddlewareRouteMatcher(matchers: MiddlewareMatcher[]): MiddlewareRouteMatch {
@@ -381,8 +392,8 @@ export function getMiddlewareRouteMatcher(matchers: MiddlewareMatcher[]): Middle
continue
}
- if (matcher.has) {
- const hasParams = matchHas(req, matcher.has, query)
+ if (matcher.has || matcher.missing) {
+ const hasParams = matchHas(req, query, matcher.has, matcher.missing)
if (!hasParams) {
continue
}
diff --git a/test/e2e/modified-tests/middleware-custom-matchers/test/index.test.ts b/test/e2e/modified-tests/middleware-custom-matchers/test/index.test.ts
index c35aee9c8d..5c74bd4662 100644
--- a/test/e2e/modified-tests/middleware-custom-matchers/test/index.test.ts
+++ b/test/e2e/modified-tests/middleware-custom-matchers/test/index.test.ts
@@ -21,7 +21,7 @@ describe('Middleware custom matchers', () => {
afterAll(() => next.destroy())
const runTests = () => {
- usuallySkip('should match missing header correctly', async () => {
+ it('should match missing header correctly', async () => {
const res = await fetchViaHTTP(next.url, '/missing-match-1')
expect(res.headers.get('x-from-middleware')).toBeDefined()
@@ -33,7 +33,7 @@ describe('Middleware custom matchers', () => {
expect(res2.headers.get('x-from-middleware')).toBeFalsy()
})
- usuallySkip('should match missing query correctly', async () => {
+ it('should match missing query correctly', async () => {
const res = await fetchViaHTTP(next.url, '/missing-match-2')
expect(res.headers.get('x-from-middleware')).toBeDefined()