From 240414452da214698ae41decfb4c16739eae9648 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 15 Aug 2022 12:55:39 +0100 Subject: [PATCH 1/9] feat: add edge middleware support to `ntl dev` --- .vscode/settings.json | 4 +- demos/middleware/middleware.ts | 17 +- demos/middleware/netlify.toml | 19 +- demos/middleware/pages/index.js | 6 + demos/middleware/tsconfig.json | 1 + package-lock.json | 277 +++++++++++++----- packages/next/src/middleware/request.ts | 20 +- packages/runtime/package.json | 4 +- packages/runtime/src/helpers/edge.ts | 28 +- packages/runtime/src/helpers/files.ts | 1 + packages/runtime/src/index.ts | 67 ++++- .../runtime/src/templates/edge/next-dev.ts | 152 ++++++++++ .../runtime/src/templates/edge/runtime.ts | 4 + packages/runtime/src/templates/edge/utils.ts | 23 +- 14 files changed, 517 insertions(+), 106 deletions(-) create mode 100644 packages/runtime/src/templates/edge/next-dev.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 928c70e5cd..935364f5f8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,8 +2,8 @@ "deno.enablePaths": [ "packages/runtime/src/templates/edge", "demos/middleware/.netlify/edge-functions", + "demos/middleware/netlify/edge-functions", "demos/server-components/.netlify/edge-functions", ], - "deno.unstable": true, - "deno.importMap": "demos/server-components/.netlify/edge-functions-import-map.json" + "deno.unstable": true } \ No newline at end of file diff --git a/demos/middleware/middleware.ts b/demos/middleware/middleware.ts index 0556e1645f..1344f60949 100644 --- a/demos/middleware/middleware.ts +++ b/demos/middleware/middleware.ts @@ -5,16 +5,13 @@ import { MiddlewareRequest } from '@netlify/next' export async function middleware(req: NextRequest) { let response - const { - nextUrl: { pathname }, - } = req + const { pathname } = req.nextUrl const request = new MiddlewareRequest(req) - if (pathname.startsWith('/static')) { // Unlike NextResponse.next(), this actually sends the request to the origin const res = await request.next() - const message = `This was static but has been transformed in ${req.geo.city}` + const message = `This was static but has been transformed in ${req.geo?.city}` // Transform the response HTML and props res.replaceText('p[id=message]', message) @@ -58,6 +55,16 @@ export async function middleware(req: NextRequest) { response.headers.set('x-modified-in-rewrite', 'true') } + if (pathname.startsWith('/shows/redirectme')) { + const url = req.nextUrl.clone() + url.pathname = '/shows/100' + response = NextResponse.redirect(url) + } + + if (pathname.startsWith('/shows/redirectexternal')) { + response = NextResponse.redirect('http://example.com/') + } + if (!response) { response = NextResponse.next() } diff --git a/demos/middleware/netlify.toml b/demos/middleware/netlify.toml index 1bb1c1f777..18ab4adc34 100644 --- a/demos/middleware/netlify.toml +++ b/demos/middleware/netlify.toml @@ -6,9 +6,8 @@ ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff [build.environment] NEXT_USE_NETLIFY_EDGE = "true" -[[plugins]] -package = "../plugin-wrapper/" - +# [[plugins]] +# package = "@netlify/plugin-nextjs" # This is a fake plugin, that makes it run npm install [[plugins]] package = "@netlify/plugin-local-install-core" @@ -19,15 +18,13 @@ included_files = [ "!node_modules/sharp/build/Release/sharp-darwin-*" ] -[dev] -framework = "#static" - +# [dev] +# framework = "#static" [[redirects]] from = "/_next/static/*" to = "/static/:splat" status = 200 - -[[redirects]] -from = "/*" -to = "/.netlify/functions/___netlify-handler" -status = 200 +# [[redirects]] +# from = "/*" +# to = "/.netlify/functions/___netlify-handler" +# status = 200 diff --git a/demos/middleware/pages/index.js b/demos/middleware/pages/index.js index 28906e2841..288eb25ffe 100644 --- a/demos/middleware/pages/index.js +++ b/demos/middleware/pages/index.js @@ -25,6 +25,12 @@ export default function Home() {

Rewrite to external URL

+

+ Redirect URL +

+

+ Redirect to external URL +

Add header to static page

diff --git a/demos/middleware/tsconfig.json b/demos/middleware/tsconfig.json index 2ea20c6c5f..d3331000f4 100644 --- a/demos/middleware/tsconfig.json +++ b/demos/middleware/tsconfig.json @@ -17,6 +17,7 @@ }, "exclude": [ "node_modules", + "netlify/edge-functions/**/*", "../../src/templates/edge/*" ], "include": [ diff --git a/package-lock.json b/package-lock.json index b8a1604bbb..74f82b44e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3694,7 +3694,6 @@ "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild/-/esbuild-0.14.25.tgz", "integrity": "sha512-ko0cMTbYpajNr0Sy6kvSqR+JDvgU/vjJhO061K1h8+Zs4MlF5AUhaITkpSOrP3g45zp++IEwN1Brxr+/BIez+g==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3732,7 +3731,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -3748,7 +3746,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -3764,7 +3761,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -3780,7 +3776,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -3796,7 +3791,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -3812,7 +3806,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -3828,7 +3821,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3844,7 +3836,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3860,7 +3851,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3876,7 +3866,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3892,7 +3881,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3908,7 +3896,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3924,7 +3911,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3940,7 +3926,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3956,7 +3941,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -3972,7 +3956,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -3988,7 +3971,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -4004,7 +3986,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -4020,7 +4001,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -4036,7 +4016,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -5224,13 +5203,13 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "devOptional": true + "dev": true }, "node_modules/@types/react": { "version": "17.0.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.48.tgz", "integrity": "sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A==", - "devOptional": true, + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5250,7 +5229,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", @@ -8781,7 +8760,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true }, "node_modules/custom-routes": { "resolved": "demos/custom-routes", @@ -12792,7 +12771,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "devOptional": true + "dev": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -19954,7 +19933,7 @@ "version": "1.50.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", - "devOptional": true, + "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -22958,10 +22937,12 @@ "version": "4.17.0", "license": "MIT", "dependencies": { + "@netlify/esbuild": "0.14.25", "@netlify/functions": "^1.2.0", "@netlify/ipx": "^1.2.2", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", "moize": "^6.1.0", @@ -22992,6 +22973,80 @@ "next": "*" } }, + "packages/runtime/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/runtime/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "packages/runtime/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/runtime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "packages/runtime/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "packages/runtime/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/runtime/node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -23005,6 +23060,54 @@ "engines": { "node": ">=10" } + }, + "packages/runtime/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "plugin": { + "name": "@netlify/plugin-nextjs", + "version": "4.14.1", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@netlify/functions": "^1.0.0", + "@netlify/ipx": "^1.2.0", + "@vercel/node-bridge": "^2.1.0", + "chalk": "^4.1.2", + "fs-extra": "^10.0.0", + "globby": "^11.0.4", + "moize": "^6.1.0", + "node-fetch": "^2.6.6", + "node-stream-zip": "^1.15.0", + "outdent": "^0.8.0", + "p-limit": "^3.1.0", + "pathe": "^0.2.0", + "pretty-bytes": "^5.6.0", + "semver": "^7.3.5", + "slash": "^3.0.0", + "tiny-glob": "^0.2.9" + }, + "devDependencies": { + "@delucis/if-env": "^1.1.2", + "@netlify/build": "^27.11.2", + "@types/fs-extra": "^9.0.13", + "@types/jest": "^27.4.1", + "@types/node": "^17.0.25", + "next": "^12.2.0", + "npm-run-all": "^4.1.5", + "typescript": "^4.6.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "next": "*" + } } }, "dependencies": { @@ -25333,8 +25436,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "dev": true, - "requires": {} + "dev": true }, "chalk": { "version": "5.0.1", @@ -25489,7 +25591,6 @@ "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild/-/esbuild-0.14.25.tgz", "integrity": "sha512-ko0cMTbYpajNr0Sy6kvSqR+JDvgU/vjJhO061K1h8+Zs4MlF5AUhaITkpSOrP3g45zp++IEwN1Brxr+/BIez+g==", - "dev": true, "requires": { "@netlify/esbuild-android-64": "0.14.25", "@netlify/esbuild-android-arm64": "0.14.25", @@ -25517,140 +25618,120 @@ "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz", "integrity": "sha512-z8vtc3jPgQxEcW9ldN5XwEPW0BHsaNFFZ4eIYSh0D2kxTCk1K2k6PY6+9+4wsCgyY0J5fnykCEjPj9AQBzCRpg==", - "dev": true, "optional": true }, "@netlify/esbuild-android-arm64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz", "integrity": "sha512-M0MHkLvOsGPano1Lpbwbik09/Dku0Pl9YJKtVZimo55/pd6kUFpktUbO+VSF9gA3ihdisEkL8/Y+gc4wxLbJkg==", - "dev": true, "optional": true }, "@netlify/esbuild-darwin-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz", "integrity": "sha512-V1GAIfYLsCIcGfGfyAQ+VhbJ/GrzrEkMamAZd5jO1I2T1XHyPMe4vYV7W7AZzcwcYzpdlj8MXIESCODlCDXnCQ==", - "dev": true, "optional": true }, "@netlify/esbuild-darwin-arm64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz", "integrity": "sha512-jfX7SY2ZD4NzSCDHZiAJfHKoqINxymToWv5LUml5/FJa6602o+x+ghg8vFezVaap1XTr+ULdFbHOEiqKpeFl+A==", - "dev": true, "optional": true }, "@netlify/esbuild-freebsd-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz", "integrity": "sha512-rsK6mW/zaFZSPVa+7CthO3bPeW6qBE9VtwHAm5tdXCP3+Qpl+9rQnbs1CEqqWGrNUv+ExlTVqrAUKkdrGq8IPg==", - "dev": true, "optional": true }, "@netlify/esbuild-freebsd-arm64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz", "integrity": "sha512-ym2Tf0dsKWJbVu3keFSs1FZezk1PXmxckuFTr0+hJMUazeNwFqJJQrY3SiN0JM7jh+VunND2RePjfsSZpcK54g==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-32": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz", "integrity": "sha512-BGRAge/+6m8/lCejgLzCdq+GpN9ah3/XBp88YGgufb4h3c2CAxrq9fIlizHyZA4THHh2T/ka3rYdBOC5ciEwEw==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz", "integrity": "sha512-yD579mskxDXrDR2vC7Dw/mEFTEuQoNYBcoKsIq+ctLiyQcKI1WCgAapJ+MCNpIDkmZp4O1uVuqIiMSyoMlv1QQ==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-arm": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz", "integrity": "sha512-NtnVECEKNr53v11E4wJzQtf7oM3HSPShDZEcwadjuK85AIJpISZcc7Hi6k/g4PsSyGjp73hH8Jly2hh+o+ruvQ==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-arm64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz", "integrity": "sha512-t1BDP9Fb94jut9m+PE4AVaTQE40JaCJEVpszvvP/6aByR5NMQ5BrNaU8e6XZ6MS7bulYsJCEcJ8I/pPraXycqg==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-mips64le": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz", "integrity": "sha512-Fo5sBkAVxxy+lEmKNo1bJD1lrVI9lpdwSzXW/I8k6ly9J8Vf2JNDYgvld4GSkNVTij5jA/zuN7aSQDEoIgx4mA==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-ppc64le": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz", "integrity": "sha512-EDInkVpAqfyfmZtYI9g9E78ohPLtyZinR19/8PGtL4zZcRUP2AnEzQRtv4NkAKAlPGa8plv3SiGsg4qKeeYRFA==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-riscv64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz", "integrity": "sha512-MACKlmgawjSkNBH34AQUNoC4CX+KD4kk5KfneiBzQeV5oUW89yBf2Q/GaqiTB58Jz93juBOkWwiV0z25AmJzvg==", - "dev": true, "optional": true }, "@netlify/esbuild-linux-s390x": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz", "integrity": "sha512-Mti6NSFGQ6GT+C9LTn15k2JttvtMcy+c1Xxqj8GYkiOqbM7Oh6NcMlXQiHxnCCsxw5Jx0WSWjdrn/dKhdiC13A==", - "dev": true, "optional": true }, "@netlify/esbuild-netbsd-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz", "integrity": "sha512-aNDKGpy926VcnA//hqw+d4k1q1ekpmhDdy0cuEib6ZS7Qb/5xGVRH6mjG8pf0TtonY9x+wiYNuQn4Dn/DwP9Kw==", - "dev": true, "optional": true }, "@netlify/esbuild-openbsd-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz", "integrity": "sha512-70W5TnRX5MroXVN0munWpF5q/AAWlamoy+PUL6cnDgc7cfnRiHHrndY++ZpWczNif8t4fQKVtC4jdUemnyb8Ag==", - "dev": true, "optional": true }, "@netlify/esbuild-sunos-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz", "integrity": "sha512-UImichNlQInjErof7tuoG/8VVbrn8Y5EVVMI4M+RoCafWh9NSl4a57hohcgwbeGwl5NcGJtHg+l/WqzlHQFFsQ==", - "dev": true, "optional": true }, "@netlify/esbuild-windows-32": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz", "integrity": "sha512-OFisPQBbuIH8wMRm//fs7wQ7d6t1PuLylIUsUSgignjEV3BOts4+pjtq0J8Aq9kkKoVp8HGSJjaxpc6v2ER/KA==", - "dev": true, "optional": true }, "@netlify/esbuild-windows-64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz", "integrity": "sha512-BgIxcEcqr4pfRc9fXStIXQVpjIkBUc3XHFEjH2t2R9pcEDU4BpMsdBgj0UA2x3Z0KtwVLLCOZDvSiaL+WkiTqA==", - "dev": true, "optional": true }, "@netlify/esbuild-windows-arm64": { "version": "0.14.25", "resolved": "https://registry.npmjs.org/@netlify/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz", "integrity": "sha512-B5Neu8aXqucUthCvAwVX7IvKbNSD/n3VFiQQcH0YQ+mtbzEIRIFaEAIanGdkmLx0shVBOlY9JxIeRThGPt2/2A==", - "dev": true, "optional": true }, "@netlify/eslint-config-node": { @@ -25696,8 +25777,7 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", - "dev": true, - "requires": {} + "dev": true }, "eslint-import-resolver-typescript": { "version": "3.3.0", @@ -25811,6 +25891,7 @@ "requires": { "@delucis/if-env": "^1.1.2", "@netlify/build": "^27.14.0", + "@netlify/esbuild": "0.14.25", "@netlify/functions": "^1.2.0", "@netlify/ipx": "^1.2.2", "@types/fs-extra": "^9.0.13", @@ -25818,6 +25899,7 @@ "@types/node": "^17.0.25", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", "moize": "^6.1.0", @@ -25835,6 +25917,53 @@ "typescript": "^4.6.3" }, "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -25842,6 +25971,11 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" } } }, @@ -26544,13 +26678,13 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "devOptional": true + "dev": true }, "@types/react": { "version": "17.0.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.48.tgz", "integrity": "sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A==", - "devOptional": true, + "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -26570,7 +26704,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "@types/sinonjs__fake-timers": { "version": "8.1.1", @@ -26878,8 +27012,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "7.2.0", @@ -29231,7 +29364,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true }, "custom-routes": { "version": "file:demos/custom-routes", @@ -30360,8 +30493,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} + "dev": true }, "eslint-formatter-codeframe": { "version": "7.32.1", @@ -30806,8 +30938,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-react": { "version": "7.29.4", @@ -30855,8 +30986,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz", "integrity": "sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-unicorn": { "version": "43.0.2", @@ -32321,7 +32451,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "devOptional": true + "dev": true }, "import-fresh": { "version": "3.3.0", @@ -33393,8 +33523,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "27.5.1", @@ -37771,7 +37900,7 @@ "version": "1.50.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", - "devOptional": true, + "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -38710,8 +38839,7 @@ "styled-jsx": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.2.tgz", - "integrity": "sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==", - "requires": {} + "integrity": "sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==" }, "supports-color": { "version": "9.2.2", @@ -39433,8 +39561,7 @@ "ws": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "requires": {} + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==" } } }, @@ -39542,8 +39669,7 @@ "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "requires": {} + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" }, "util-deprecate": { "version": "1.0.2", @@ -39947,8 +40073,7 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} + "dev": true }, "xdg-basedir": { "version": "4.0.0", diff --git a/packages/next/src/middleware/request.ts b/packages/next/src/middleware/request.ts index 3b15ff039e..76847466a3 100644 --- a/packages/next/src/middleware/request.ts +++ b/packages/next/src/middleware/request.ts @@ -4,9 +4,18 @@ import type { NextRequest } from 'next/server' import { MiddlewareResponse } from './response' +export interface NextOptions { + /** + * Include conditional request headers in the request to the origin. + * If you do this, you must ensure you check the response for a 304 Not Modified response + * and handle it and the missing bode accordingly. + */ + sendConditionalRequest?: boolean +} + // TODO: add Context type type Context = { - next: () => Promise + next: (options?: NextOptions) => Promise } /** @@ -40,9 +49,14 @@ export class MiddlewareRequest extends Request { }) } - async next(): Promise { + /** + * + * @param options + * @returns + */ + async next(options?: NextOptions): Promise { this.applyHeaders() - const response = await this.context.next() + const response = await this.context.next(options) return new MiddlewareResponse(response) } diff --git a/packages/runtime/package.json b/packages/runtime/package.json index a8b5b0bbc4..645384e488 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -9,10 +9,12 @@ "manifest.yml" ], "dependencies": { + "@netlify/esbuild": "0.14.25", "@netlify/functions": "^1.2.0", "@netlify/ipx": "^1.2.2", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", "moize": "^6.1.0", @@ -22,8 +24,8 @@ "p-limit": "^3.1.0", "pathe": "^0.2.0", "pretty-bytes": "^5.6.0", - "slash": "^3.0.0", "semver": "^7.3.5", + "slash": "^3.0.0", "tiny-glob": "^0.2.9" }, "devDependencies": { diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index 77b0da69bd..4855cc8018 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -2,7 +2,7 @@ import { promises as fs, existsSync } from 'fs' import { resolve, join } from 'path' -import type { NetlifyConfig } from '@netlify/build' +import type { NetlifyConfig, NetlifyPluginConstants } from '@netlify/build' import { copyFile, emptyDir, ensureDir, readJSON, readJson, writeJSON, writeJson } from 'fs-extra' import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin' @@ -20,6 +20,7 @@ export interface FunctionManifest { pattern: string } > + import_map?: string } export const loadMiddlewareManifest = (netlifyConfig: NetlifyConfig): Promise => { @@ -122,6 +123,31 @@ const writeEdgeFunction = async ({ } } +export const writeDevEdgeFunction = async ({ + INTERNAL_EDGE_FUNCTIONS_SRC = '.netlify/edge-functions', +}: NetlifyPluginConstants & { + // The constants type needs an update + INTERNAL_EDGE_FUNCTIONS_SRC?: string +}) => { + const manifest: FunctionManifest = { + functions: [ + { + function: 'next-dev', + path: '/*', + }, + ], + version: 1, + } + const edgeFunctionRoot = resolve(INTERNAL_EDGE_FUNCTIONS_SRC) + await emptyDir(edgeFunctionRoot) + await writeJson(join(edgeFunctionRoot, 'manifest.json'), manifest) + + const edgeFunctionDir = join(edgeFunctionRoot, 'next-dev') + await ensureDir(edgeFunctionDir) + await copyEdgeSourceFile({ edgeFunctionDir, file: 'next-dev.ts', target: 'index.ts' }) + await copyEdgeSourceFile({ edgeFunctionDir, file: 'utils.ts' }) +} + /** * Writes Edge Functions for the Next middleware */ diff --git a/packages/runtime/src/helpers/files.ts b/packages/runtime/src/helpers/files.ts index 2629eb6e0c..19958e943c 100644 --- a/packages/runtime/src/helpers/files.ts +++ b/packages/runtime/src/helpers/files.ts @@ -377,6 +377,7 @@ export const patchNextFiles = async (root: string): Promise => { } export const unpatchFile = async (file: string): Promise => { + return const origFile = `${file}.orig` if (existsSync(origFile)) { await move(origFile, file, { overwrite: true }) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 2915ba2763..c834baa60e 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,9 +1,10 @@ /* eslint-disable max-lines */ -import { join, relative } from 'path' +import { join, relative, resolve } from 'path' -import type { NetlifyPlugin } from '@netlify/build' +import type { NetlifyPlugin, OnPreBuild } from '@netlify/build' import { greenBright, yellowBright, bold } from 'chalk' -import { existsSync, readFileSync } from 'fs-extra' +import execa from 'execa' +import { existsSync, readFileSync, unlink } from 'fs-extra' import { outdent } from 'outdent' import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from './constants' @@ -15,7 +16,7 @@ import { configureHandlerFunctions, generateCustomHeaders, } from './helpers/config' -import { updateConfig, writeEdgeFunctions, loadMiddlewareManifest } from './helpers/edge' +import { updateConfig, writeEdgeFunctions, loadMiddlewareManifest, writeDevEdgeFunction } from './helpers/edge' import { moveStaticPages, movePublicFiles, patchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' @@ -30,7 +31,7 @@ import { warnForRootRedirects, } from './helpers/verification' -const plugin: NetlifyPlugin = { +const plugin: NetlifyPlugin & { onPreDev?: OnPreBuild; onDev?: OnPreBuild } = { async onPreBuild({ constants, netlifyConfig, @@ -214,5 +215,59 @@ const plugin: NetlifyPlugin = { warnForRootRedirects({ appDir }) }, } -module.exports = plugin +// The types haven't been updated yet +const nextPlugin = (_inputs, { events }: { events: Set }): NetlifyPlugin & { onPreDev?: OnPreBuild } => { + if (!events.has('onPreDev')) { + return { + ...plugin, + onEnd: ({ utils }) => { + utils.status.show({ + title: 'Please upgrade to the latest version of the Netlify CLI', + summary: + 'To support for the latest Next.js features, please upgrade to the latest version of the Netlify CLI', + }) + }, + } + } + return { + ...plugin, + onPreDev: async ({ constants, netlifyConfig }) => { + // Need to patch the files, because build might not have been run + await patchNextFiles(resolve(netlifyConfig.build.publish, '..')) + + // Clean up old functions + await unlink(resolve('.netlify', 'middleware.js')).catch(() => { + // Ignore if it doesn't exist + }) + await writeDevEdgeFunction(constants) + if ( + !existsSync(resolve(netlifyConfig.build.base, 'middleware.ts')) && + !existsSync(resolve(netlifyConfig.build.base, 'middleware.js')) + ) { + console.log( + "No middleware found. Create a 'middleware.ts' or 'middleware.js' file in your project root to add custom middleware.", + ) + } else { + console.log('Watching for changes in Next.js middleware...') + } + // Eventually we might want to do this via esbuild's API, but for now the CLI works fine + const childProcess = execa(`esbuild`, [ + `--bundle`, + `--outdir=${resolve('.netlify')}`, + `--format=esm`, + '--watch', + // Watch for both, because it can have either ts or js + resolve(netlifyConfig.build.base, 'middleware.ts'), + resolve(netlifyConfig.build.base, 'middleware.js'), + ]) + + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + // Don't return the promise because we don't want to wait for the child process to finish + }, + } +} + +module.exports = nextPlugin + /* eslint-enable max-lines */ diff --git a/packages/runtime/src/templates/edge/next-dev.ts b/packages/runtime/src/templates/edge/next-dev.ts new file mode 100644 index 0000000000..f953143fdb --- /dev/null +++ b/packages/runtime/src/templates/edge/next-dev.ts @@ -0,0 +1,152 @@ +import type { Context } from 'https://edge.netlify.com' +import { NextRequest, NextResponse } from 'https://esm.sh/next/server' +import { fromFileUrl } from 'https://deno.land/std/path/mod.ts' +import { buildResponse } from './utils.ts' + +export interface FetchEventResult { + response: Response + waitUntil: Promise +} + +interface I18NConfig { + defaultLocale: string + domains?: DomainLocale[] + localeDetection?: false + locales: string[] +} + +interface DomainLocale { + defaultLocale: string + domain: string + http?: true + locales?: string[] +} +export interface NextRequestInit extends RequestInit { + geo?: { + city?: string + country?: string + region?: string + } + ip?: string + nextConfig?: { + basePath?: string + i18n?: I18NConfig | null + trailingSlash?: boolean + } +} + +export interface RequestData { + geo?: { + city?: string + country?: string + region?: string + latitude?: string + longitude?: string + } + headers: Record + ip?: string + method: string + nextConfig?: { + basePath?: string + i18n?: Record + trailingSlash?: boolean + } + page?: { + name?: string + params?: { [key: string]: string } + } + url: string + body?: ReadableStream +} + +export interface RequestContext { + request: Request + context: Context +} + +declare global { + // deno-lint-ignore no-var + var NFRequestContextMap: Map + // deno-lint-ignore no-var + var __dirname: string +} + +globalThis.NFRequestContextMap ||= new Map() +globalThis.__dirname = fromFileUrl(new URL('./', import.meta.url)).slice(0, -1) + +// Check if a file exists, given a relative path +const exists = async (relativePath: string) => { + const path = fromFileUrl(new URL(relativePath, import.meta.url)) + try { + await Deno.stat(path) + return true + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + return false + } + throw error + } +} + +const handler = async (req: Request, context: Context) => { + // if (!Deno.env.get('NETLIFY_DEV')) { + // return + // } + let middleware + // Dynamic imports and FS operations aren't allowed when deployed, + // but that's fine because this is only ever used locally. + // We don't want to just try importing and use that to test, + // because that would also throw if there's an error in the middleware, + // which we would want to surface not ignore. + if (await exists('../../iddleware.js')) { + // These will be user code + const nextMiddleware = await import('../../middleware.js') + middleware = nextMiddleware.middleware + } else { + // No middleware, so we silently return + return + } + + // This is the format expected by Next.js + const geo: NextRequestInit['geo'] = { + country: context.geo.country?.code, + region: context.geo.subdivision?.code, + city: context.geo.city, + } + + // A default request id is fine locally + const requestId = req.headers.get('x-nf-request-id') || 'request-id' + + globalThis.NFRequestContextMap.set(requestId, { + request: req, + context, + }) + + const request: NextRequestInit = { + headers: Object.fromEntries(req.headers.entries()), + geo, + method: req.method, + ip: context.ip, + body: req.body || undefined, + } + + const nextRequest: NextRequest = new NextRequest(req, request) + + try { + const response = await middleware(nextRequest) + return buildResponse({ + result: { response: response || NextResponse.next(), waitUntil: Promise.resolve() }, + request: req, + context, + }) + } catch (error) { + console.error(error) + return new Response(error.message, { status: 500 }) + } finally { + if (requestId) { + globalThis.NFRequestContextMap.delete(requestId) + } + } +} + +export default handler diff --git a/packages/runtime/src/templates/edge/runtime.ts b/packages/runtime/src/templates/edge/runtime.ts index da543acd14..01c16b7121 100644 --- a/packages/runtime/src/templates/edge/runtime.ts +++ b/packages/runtime/src/templates/edge/runtime.ts @@ -45,6 +45,10 @@ declare global { globalThis.NFRequestContextMap ||= new Map() const handler = async (req: Request, context: Context) => { + if (Deno.env.get('NETLIFY_DEV')) { + console.log('is dev') + return + } const url = new URL(req.url) const geo = { diff --git a/packages/runtime/src/templates/edge/utils.ts b/packages/runtime/src/templates/edge/utils.ts index 582eb50ae7..9897c77c03 100644 --- a/packages/runtime/src/templates/edge/utils.ts +++ b/packages/runtime/src/templates/edge/utils.ts @@ -123,17 +123,38 @@ export const buildResponse = async ({ request.headers.set('x-nf-next-middleware', 'skip') const rewrite = res.headers.get('x-middleware-rewrite') + + // Data requests (i.e. requests for /_next/data ) need special handling + const isDataReq = request.headers.get('x-nextjs-data') + if (rewrite) { const rewriteUrl = new URL(rewrite, request.url) const baseUrl = new URL(request.url) + const relativeUrl = relativizeURL(rewrite, request.url) + + // Data requests might be rewritten to an external URL + // This header tells the client router the redirect target, and if it's external then it will do a full navigation + if (isDataReq) { + res.headers.set('x-nextjs-rewrite', relativeUrl) + } if (rewriteUrl.hostname !== baseUrl.hostname) { // Netlify Edge Functions don't support proxying to external domains, but Next middleware does const proxied = fetch(new Request(rewriteUrl.toString(), request)) return addMiddlewareHeaders(proxied, res) } - res.headers.set('x-middleware-rewrite', relativizeURL(rewrite, request.url)) + res.headers.set('x-middleware-rewrite', relativeUrl) + return addMiddlewareHeaders(context.rewrite(rewrite), res) } + + const redirect = res.headers.get('Location') + + // Data requests shouldn;t automatically redirect in the browser (they might be HTML pages): they're handled by the router + if (redirect && isDataReq) { + res.headers.delete('location') + res.headers.set('x-nextjs-redirect', relativizeURL(redirect, request.url)) + } + if (res.headers.get('x-middleware-next') === '1') { return addMiddlewareHeaders(context.next(), res) } From 5dd37babef57b14fbe77ea118241e331cba6ddcc Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Wed, 17 Aug 2022 17:28:23 +0100 Subject: [PATCH 2/9] chore: typo --- packages/runtime/src/templates/edge/next-dev.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/src/templates/edge/next-dev.ts b/packages/runtime/src/templates/edge/next-dev.ts index f953143fdb..5e4a5a02fa 100644 --- a/packages/runtime/src/templates/edge/next-dev.ts +++ b/packages/runtime/src/templates/edge/next-dev.ts @@ -98,7 +98,7 @@ const handler = async (req: Request, context: Context) => { // We don't want to just try importing and use that to test, // because that would also throw if there's an error in the middleware, // which we would want to surface not ignore. - if (await exists('../../iddleware.js')) { + if (await exists('../../middleware.js')) { // These will be user code const nextMiddleware = await import('../../middleware.js') middleware = nextMiddleware.middleware From aaac07adc0bcff9f4da4a72eae9f101dbb2dbf43 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Wed, 17 Aug 2022 17:31:52 +0100 Subject: [PATCH 3/9] chore: null check for plugin instantiation --- packages/runtime/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index c834baa60e..9511361335 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -216,8 +216,8 @@ const plugin: NetlifyPlugin & { onPreDev?: OnPreBuild; onDev?: OnPreBuild } = { }, } // The types haven't been updated yet -const nextPlugin = (_inputs, { events }: { events: Set }): NetlifyPlugin & { onPreDev?: OnPreBuild } => { - if (!events.has('onPreDev')) { +const nextPlugin = (_inputs, meta: { events: Set }): NetlifyPlugin & { onPreDev?: OnPreBuild } => { + if (!meta?.events?.has('onPreDev')) { return { ...plugin, onEnd: ({ utils }) => { From bc5713735d3a40c17f3ec2879b78893232de3044 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Wed, 17 Aug 2022 20:16:45 +0100 Subject: [PATCH 4/9] chore: use plugin wrapper --- demos/middleware/netlify.toml | 14 +++++++------- packages/runtime/src/helpers/files.ts | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/demos/middleware/netlify.toml b/demos/middleware/netlify.toml index 18ab4adc34..e0b4aa21ce 100644 --- a/demos/middleware/netlify.toml +++ b/demos/middleware/netlify.toml @@ -6,8 +6,9 @@ ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff [build.environment] NEXT_USE_NETLIFY_EDGE = "true" -# [[plugins]] -# package = "@netlify/plugin-nextjs" +[[plugins]] +package = "../plugin-wrapper/" + # This is a fake plugin, that makes it run npm install [[plugins]] package = "@netlify/plugin-local-install-core" @@ -17,13 +18,12 @@ included_files = [ "!node_modules/sharp/vendor/8.12.2/darwin-*/**/*", "!node_modules/sharp/build/Release/sharp-darwin-*" ] - # [dev] # framework = "#static" -[[redirects]] -from = "/_next/static/*" -to = "/static/:splat" -status = 200 +# [[redirects]] +# from = "/_next/static/*" +# to = "/static/:splat" +# status = 200 # [[redirects]] # from = "/*" # to = "/.netlify/functions/___netlify-handler" diff --git a/packages/runtime/src/helpers/files.ts b/packages/runtime/src/helpers/files.ts index 19958e943c..2629eb6e0c 100644 --- a/packages/runtime/src/helpers/files.ts +++ b/packages/runtime/src/helpers/files.ts @@ -377,7 +377,6 @@ export const patchNextFiles = async (root: string): Promise => { } export const unpatchFile = async (file: string): Promise => { - return const origFile = `${file}.orig` if (existsSync(origFile)) { await move(origFile, file, { overwrite: true }) From cce9c0e06fbb5b9232fd006d699e6214a512ea30 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 18 Aug 2022 11:32:56 +0100 Subject: [PATCH 5/9] chore: refactor --- .vscode/settings.json | 1 - demos/middleware/netlify.toml | 9 ++--- demos/middleware/tsconfig.json | 1 - packages/next/src/middleware/request.ts | 4 +-- packages/runtime/src/helpers/dev.ts | 44 +++++++++++++++++++++++++ packages/runtime/src/index.ts | 43 +++--------------------- 6 files changed, 53 insertions(+), 49 deletions(-) create mode 100644 packages/runtime/src/helpers/dev.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 935364f5f8..032d621103 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,6 @@ "deno.enablePaths": [ "packages/runtime/src/templates/edge", "demos/middleware/.netlify/edge-functions", - "demos/middleware/netlify/edge-functions", "demos/server-components/.netlify/edge-functions", ], "deno.unstable": true diff --git a/demos/middleware/netlify.toml b/demos/middleware/netlify.toml index e0b4aa21ce..514076a4c0 100644 --- a/demos/middleware/netlify.toml +++ b/demos/middleware/netlify.toml @@ -3,12 +3,8 @@ command = "npm run build" publish = ".next" ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;" -[build.environment] -NEXT_USE_NETLIFY_EDGE = "true" - -[[plugins]] -package = "../plugin-wrapper/" - +# [[plugins]] +# package = "../plugin-wrapper/" # This is a fake plugin, that makes it run npm install [[plugins]] package = "@netlify/plugin-local-install-core" @@ -18,6 +14,7 @@ included_files = [ "!node_modules/sharp/vendor/8.12.2/darwin-*/**/*", "!node_modules/sharp/build/Release/sharp-darwin-*" ] +# Uncomment this if testing the built files rather than dev # [dev] # framework = "#static" # [[redirects]] diff --git a/demos/middleware/tsconfig.json b/demos/middleware/tsconfig.json index d3331000f4..2ea20c6c5f 100644 --- a/demos/middleware/tsconfig.json +++ b/demos/middleware/tsconfig.json @@ -17,7 +17,6 @@ }, "exclude": [ "node_modules", - "netlify/edge-functions/**/*", "../../src/templates/edge/*" ], "include": [ diff --git a/packages/next/src/middleware/request.ts b/packages/next/src/middleware/request.ts index 76847466a3..c323d607cd 100644 --- a/packages/next/src/middleware/request.ts +++ b/packages/next/src/middleware/request.ts @@ -50,9 +50,7 @@ export class MiddlewareRequest extends Request { } /** - * - * @param options - * @returns + * Passes the request to the origin, allowing you to access the response */ async next(options?: NextOptions): Promise { this.applyHeaders() diff --git a/packages/runtime/src/helpers/dev.ts b/packages/runtime/src/helpers/dev.ts new file mode 100644 index 0000000000..4e77660e8b --- /dev/null +++ b/packages/runtime/src/helpers/dev.ts @@ -0,0 +1,44 @@ +import { resolve } from 'path' + +import { NetlifyPlugin } from '@netlify/build' +import execa from 'execa' +import { unlink, existsSync } from 'fs-extra' + +import { writeDevEdgeFunction } from './edge' +import { patchNextFiles } from './files' + +// The types haven't been updated yet +export const onPreDev: NetlifyPlugin['onPreBuild'] = async ({ constants, netlifyConfig }) => { + // Need to patch the files, because build might not have been run + await patchNextFiles(resolve(netlifyConfig.build.publish, '..')) + + // Clean up old functions + await unlink(resolve('.netlify', 'middleware.js')).catch(() => { + // Ignore if it doesn't exist + }) + await writeDevEdgeFunction(constants) + if ( + !existsSync(resolve(netlifyConfig.build.base, 'middleware.ts')) && + !existsSync(resolve(netlifyConfig.build.base, 'middleware.js')) + ) { + console.log( + "No middleware found. Create a 'middleware.ts' or 'middleware.js' file in your project root to add custom middleware.", + ) + } else { + console.log('Watching for changes in Next.js middleware...') + } + // Eventually we might want to do this via esbuild's API, but for now the CLI works fine + const childProcess = execa(`esbuild`, [ + `--bundle`, + `--outdir=${resolve('.netlify')}`, + `--format=esm`, + '--watch', + // Watch for both, because it can have either ts or js + resolve(netlifyConfig.build.base, 'middleware.ts'), + resolve(netlifyConfig.build.base, 'middleware.js'), + ]) + + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + // Don't return the promise because we don't want to wait for the child process to finish +} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 9bed43b33a..ae89281196 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,10 +1,9 @@ /* eslint-disable max-lines */ -import { join, relative, resolve } from 'path' +import { join, relative } from 'path' import type { NetlifyPlugin, OnPreBuild } from '@netlify/build' import { greenBright, yellowBright, bold } from 'chalk' -import execa from 'execa' -import { existsSync, readFileSync, unlink } from 'fs-extra' +import { existsSync, readFileSync } from 'fs-extra' import { outdent } from 'outdent' import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from './constants' @@ -16,7 +15,8 @@ import { configureHandlerFunctions, generateCustomHeaders, } from './helpers/config' -import { updateConfig, writeEdgeFunctions, loadMiddlewareManifest, writeDevEdgeFunction } from './helpers/edge' +import { onPreDev } from './helpers/dev' +import { updateConfig, writeEdgeFunctions, loadMiddlewareManifest } from './helpers/edge' import { moveStaticPages, movePublicFiles, patchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' @@ -219,40 +219,7 @@ const nextPlugin = (_inputs, meta: { events: Set }): NetlifyPlugin & { o } return { ...plugin, - onPreDev: async ({ constants, netlifyConfig }) => { - // Need to patch the files, because build might not have been run - await patchNextFiles(resolve(netlifyConfig.build.publish, '..')) - - // Clean up old functions - await unlink(resolve('.netlify', 'middleware.js')).catch(() => { - // Ignore if it doesn't exist - }) - await writeDevEdgeFunction(constants) - if ( - !existsSync(resolve(netlifyConfig.build.base, 'middleware.ts')) && - !existsSync(resolve(netlifyConfig.build.base, 'middleware.js')) - ) { - console.log( - "No middleware found. Create a 'middleware.ts' or 'middleware.js' file in your project root to add custom middleware.", - ) - } else { - console.log('Watching for changes in Next.js middleware...') - } - // Eventually we might want to do this via esbuild's API, but for now the CLI works fine - const childProcess = execa(`esbuild`, [ - `--bundle`, - `--outdir=${resolve('.netlify')}`, - `--format=esm`, - '--watch', - // Watch for both, because it can have either ts or js - resolve(netlifyConfig.build.base, 'middleware.ts'), - resolve(netlifyConfig.build.base, 'middleware.js'), - ]) - - childProcess.stdout.pipe(process.stdout) - childProcess.stderr.pipe(process.stderr) - // Don't return the promise because we don't want to wait for the child process to finish - }, + onPreDev, } } From 6748bd433b4446e1242904d07c6b467b3ec9cd2e Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 18 Aug 2022 11:37:27 +0100 Subject: [PATCH 6/9] chore: fix test --- packages/runtime/src/index.ts | 4 ++-- test/index.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index ae89281196..507783460c 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -204,7 +204,7 @@ const plugin: NetlifyPlugin & { onPreDev?: OnPreBuild; onDev?: OnPreBuild } = { }, } // The types haven't been updated yet -const nextPlugin = (_inputs, meta: { events: Set }): NetlifyPlugin & { onPreDev?: OnPreBuild } => { +const nextRuntime = (_inputs, meta: { events?: Set } = {}): NetlifyPlugin & { onPreDev?: OnPreBuild } => { if (!meta?.events?.has('onPreDev')) { return { ...plugin, @@ -223,6 +223,6 @@ const nextPlugin = (_inputs, meta: { events: Set }): NetlifyPlugin & { o } } -module.exports = nextPlugin +module.exports = nextRuntime /* eslint-enable max-lines */ diff --git a/test/index.js b/test/index.js index e071c23b10..b2542e2e06 100644 --- a/test/index.js +++ b/test/index.js @@ -14,7 +14,8 @@ const cpy = require('cpy') const { dir: getTmpDir } = require('tmp-promise') const { downloadFile } = require('../packages/runtime/src/templates/handlerUtils') -const nextRuntime = require('../packages/runtime/src') +const nextRuntimeFactory = require('../packages/runtime/src') +const nextRuntime = nextRuntimeFactory({}) const { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME } = require('../packages/runtime/src/constants') const { join } = require('pathe') @@ -33,8 +34,6 @@ const { } = require('../packages/runtime/src/helpers/config') const { dirname } = require('path') const { getProblematicUserRewrites } = require('../packages/runtime/src/helpers/verification') -const { onPostBuild } = require('../packages/runtime/lib') -const { basePath } = require('../demos/next-i18next/next.config') const chance = new Chance() const FIXTURES_DIR = `${__dirname}/fixtures` From 398efb12c6d0f29fe6d06769a69ba5df0a7babc7 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 18 Aug 2022 11:52:51 +0100 Subject: [PATCH 7/9] chore: update checks --- packages/runtime/src/helpers/dev.ts | 4 ++-- packages/runtime/src/index.ts | 7 +++++-- packages/runtime/src/templates/edge/next-dev.ts | 8 +++++--- packages/runtime/src/templates/edge/runtime.ts | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/runtime/src/helpers/dev.ts b/packages/runtime/src/helpers/dev.ts index 4e77660e8b..c029b0a0e9 100644 --- a/packages/runtime/src/helpers/dev.ts +++ b/packages/runtime/src/helpers/dev.ts @@ -1,6 +1,6 @@ import { resolve } from 'path' -import { NetlifyPlugin } from '@netlify/build' +import { OnPreBuild } from '@netlify/build' import execa from 'execa' import { unlink, existsSync } from 'fs-extra' @@ -8,7 +8,7 @@ import { writeDevEdgeFunction } from './edge' import { patchNextFiles } from './files' // The types haven't been updated yet -export const onPreDev: NetlifyPlugin['onPreBuild'] = async ({ constants, netlifyConfig }) => { +export const onPreDev: OnPreBuild = async ({ constants, netlifyConfig }) => { // Need to patch the files, because build might not have been run await patchNextFiles(resolve(netlifyConfig.build.publish, '..')) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index bc81e7654c..b09960271b 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -31,7 +31,7 @@ import { warnForRootRedirects, } from './helpers/verification' -const plugin: NetlifyPlugin & { onPreDev?: OnPreBuild; onDev?: OnPreBuild } = { +const plugin: NetlifyPlugin = { async onPreBuild({ constants, netlifyConfig, @@ -220,7 +220,10 @@ const plugin: NetlifyPlugin & { onPreDev?: OnPreBuild; onDev?: OnPreBuild } = { }, } // The types haven't been updated yet -const nextRuntime = (_inputs, meta: { events?: Set } = {}): NetlifyPlugin & { onPreDev?: OnPreBuild } => { +const nextRuntime = ( + _inputs, + meta: { events?: Set } = {}, +): NetlifyPlugin & { onPreDev?: NetlifyPlugin['onPreBuild'] } => { if (!meta?.events?.has('onPreDev')) { return { ...plugin, diff --git a/packages/runtime/src/templates/edge/next-dev.ts b/packages/runtime/src/templates/edge/next-dev.ts index 5e4a5a02fa..34cf5501f5 100644 --- a/packages/runtime/src/templates/edge/next-dev.ts +++ b/packages/runtime/src/templates/edge/next-dev.ts @@ -89,9 +89,11 @@ const exists = async (relativePath: string) => { } const handler = async (req: Request, context: Context) => { - // if (!Deno.env.get('NETLIFY_DEV')) { - // return - // } + if (!Deno.env.get('NETLIFY_DEV')) { + // Only run in dev + return + } + let middleware // Dynamic imports and FS operations aren't allowed when deployed, // but that's fine because this is only ever used locally. diff --git a/packages/runtime/src/templates/edge/runtime.ts b/packages/runtime/src/templates/edge/runtime.ts index 01c16b7121..7f33da2954 100644 --- a/packages/runtime/src/templates/edge/runtime.ts +++ b/packages/runtime/src/templates/edge/runtime.ts @@ -46,7 +46,7 @@ globalThis.NFRequestContextMap ||= new Map() const handler = async (req: Request, context: Context) => { if (Deno.env.get('NETLIFY_DEV')) { - console.log('is dev') + // Don't run in dev return } const url = new URL(req.url) From 38daf34c3d6914f664025b4ef4fa040426c7b581 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 18 Aug 2022 12:06:07 +0100 Subject: [PATCH 8/9] chore: use opt-out for edge at build/dev time --- demos/middleware/netlify.toml | 5 +---- packages/runtime/src/helpers/edge.ts | 2 -- packages/runtime/src/helpers/files.ts | 8 ++++---- packages/runtime/src/templates/edge/next-dev.ts | 9 +++++---- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/demos/middleware/netlify.toml b/demos/middleware/netlify.toml index a92679a893..ce61ea4342 100644 --- a/demos/middleware/netlify.toml +++ b/demos/middleware/netlify.toml @@ -3,11 +3,8 @@ command = "npm run build" publish = ".next" ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;" -[build.environment] -NEXT_USE_NETLIFY_EDGE = "true" - [[plugins]] -package = "../plugin-wrapper/" +package = "@netlify/plugin-nextjs" # This is a fake plugin, that makes it run npm install [[plugins]] diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index 601c431daf..a0e23aeef5 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -211,8 +211,6 @@ export const writeEdgeFunctions = async (netlifyConfig: NetlifyConfig) => { export const enableEdgeInNextConfig = async (publish: string) => { const configFile = join(publish, 'required-server-files.json') const config = await readJSON(configFile) - // This is for runtime in Next.js, rather than a build plugin setting - config.config.env.NEXT_USE_NETLIFY_EDGE = 'true' await writeJSON(configFile, config) } /* eslint-enable max-lines */ diff --git a/packages/runtime/src/helpers/files.ts b/packages/runtime/src/helpers/files.ts index 8c9a84d847..f1761fdcee 100644 --- a/packages/runtime/src/helpers/files.ts +++ b/packages/runtime/src/helpers/files.ts @@ -340,19 +340,19 @@ const baseServerReplacements: Array<[string, string]> = [ const nextServerReplacements: Array<[string, string]> = [ [ `getMiddlewareManifest() {\n if (this.minimalMode) return null;`, - `getMiddlewareManifest() {\n if (this.minimalMode || process.env.NEXT_USE_NETLIFY_EDGE) return null;`, + `getMiddlewareManifest() {\n if (this.minimalMode || !process.env.NEXT_DISABLE_NETLIFY_EDGE) return null;`, ], [ `generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode) return []`, - `generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode || process.env.NEXT_USE_NETLIFY_EDGE) return [];`, + `generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode || !process.env.NEXT_DISABLE_NETLIFY_EDGE) return [];`, ], [ `generateCatchAllMiddlewareRoute() {\n if (this.minimalMode) return undefined;`, - `generateCatchAllMiddlewareRoute() {\n if (this.minimalMode || process.env.NEXT_USE_NETLIFY_EDGE) return undefined;`, + `generateCatchAllMiddlewareRoute() {\n if (this.minimalMode || !process.env.NEXT_DISABLE_NETLIFY_EDGE) return undefined;`, ], [ `getMiddlewareManifest() {\n if (this.minimalMode) {`, - `getMiddlewareManifest() {\n if (!this.minimalMode && !process.env.NEXT_USE_NETLIFY_EDGE) {`, + `getMiddlewareManifest() {\n if (!this.minimalMode && process.env.NEXT_DISABLE_NETLIFY_EDGE) {`, ], ] diff --git a/packages/runtime/src/templates/edge/next-dev.ts b/packages/runtime/src/templates/edge/next-dev.ts index 34cf5501f5..b640054c6e 100644 --- a/packages/runtime/src/templates/edge/next-dev.ts +++ b/packages/runtime/src/templates/edge/next-dev.ts @@ -89,10 +89,11 @@ const exists = async (relativePath: string) => { } const handler = async (req: Request, context: Context) => { - if (!Deno.env.get('NETLIFY_DEV')) { - // Only run in dev - return - } + // Uncomment when CLI update lands + // if (!Deno.env.get('NETLIFY_DEV')) { + // // Only run in dev + // return + // } let middleware // Dynamic imports and FS operations aren't allowed when deployed, From fc0c68b9f95f4d2d6aeb96c5d50bc9c2c334e122 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 18 Aug 2022 12:17:46 +0100 Subject: [PATCH 9/9] chore: make build work --- demos/middleware/netlify.toml | 4 +++- demos/middleware/package.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/demos/middleware/netlify.toml b/demos/middleware/netlify.toml index ce61ea4342..ea420c6e94 100644 --- a/demos/middleware/netlify.toml +++ b/demos/middleware/netlify.toml @@ -4,7 +4,9 @@ publish = ".next" ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;" [[plugins]] -package = "@netlify/plugin-nextjs" +# Switch these when testing `ntl dev` +# package = "@netlify/plugin-nextjs" +package = "../plugin-wrapper/" # This is a fake plugin, that makes it run npm install [[plugins]] diff --git a/demos/middleware/package.json b/demos/middleware/package.json index e34818bf73..bef88c4795 100644 --- a/demos/middleware/package.json +++ b/demos/middleware/package.json @@ -9,8 +9,8 @@ "ntl": "ntl-internal" }, "dependencies": { - "@netlify/plugin-nextjs": "*", "@netlify/next": "*", + "@netlify/plugin-nextjs": "*", "next": "^12.2.0", "react": "18.0.0", "react-dom": "18.0.0" @@ -24,4 +24,4 @@ "npm-run-all": "^4.1.5", "typescript": "^4.6.3" } -} \ No newline at end of file +}