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()