diff --git a/package-lock.json b/package-lock.json index d7792e4f84..10842637f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5556,13 +5556,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.50", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.50.tgz", "integrity": "sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5588,7 +5588,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", @@ -9128,7 +9128,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", @@ -13154,7 +13154,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", @@ -20324,7 +20324,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", @@ -23309,7 +23309,7 @@ }, "packages/next": { "name": "@netlify/next", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "devDependencies": { "@types/node": "^17.0.25", @@ -23326,7 +23326,7 @@ }, "packages/runtime": { "name": "@netlify/plugin-nextjs", - "version": "4.23.1", + "version": "4.23.3", "license": "MIT", "dependencies": { "@netlify/esbuild": "0.14.39", @@ -23334,6 +23334,7 @@ "@netlify/ipx": "^1.2.5", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "destr": "^1.1.1", "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", @@ -25833,8 +25834,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", @@ -26185,8 +26185,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", @@ -26315,6 +26314,7 @@ "@types/node": "^17.0.25", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "destr": "*", "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", @@ -27099,13 +27099,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.50", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.50.tgz", "integrity": "sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA==", - "devOptional": true, + "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -27131,7 +27131,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", @@ -27439,8 +27439,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", @@ -29801,7 +29800,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", @@ -30931,8 +30930,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", @@ -31377,8 +31375,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.1.tgz", "integrity": "sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-react": { "version": "7.29.4", @@ -31426,8 +31423,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", @@ -32900,7 +32896,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", @@ -33962,8 +33958,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", @@ -38385,7 +38380,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", @@ -38646,14 +38641,12 @@ "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==" }, "use-sync-external-store": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz", - "integrity": "sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==", - "requires": {} + "integrity": "sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==" } } }, @@ -39465,8 +39458,7 @@ "styled-jsx": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.6.tgz", - "integrity": "sha512-xOeROtkK5MGMDimBQ3J6iPId8q0t/BDoG5XN6oKkZClVz9ISF/hihN8OCn2LggMU6N32aXnrXBdn3auSqNS9fA==", - "requires": {} + "integrity": "sha512-xOeROtkK5MGMDimBQ3J6iPId8q0t/BDoG5XN6oKkZClVz9ISF/hihN8OCn2LggMU6N32aXnrXBdn3auSqNS9fA==" }, "supports-color": { "version": "9.2.2", @@ -40188,8 +40180,7 @@ "ws": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "requires": {} + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==" } } }, @@ -40297,8 +40288,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", @@ -40703,8 +40693,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/runtime/package.json b/packages/runtime/package.json index a030d887c1..8eb352bb28 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -15,6 +15,7 @@ "@netlify/ipx": "^1.2.5", "@vercel/node-bridge": "^2.1.0", "chalk": "^4.1.2", + "destr": "^1.1.1", "execa": "^5.1.1", "fs-extra": "^10.0.0", "globby": "^11.0.4", @@ -63,4 +64,4 @@ "engines": { "node": ">=12.0.0" } -} \ No newline at end of file +} diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index cafe4a3d6f..3829420f4b 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -3,9 +3,12 @@ import { promises as fs, existsSync } from 'fs' import { resolve, join } from 'path' import type { NetlifyConfig, NetlifyPluginConstants } from '@netlify/build' +import { greenBright } from 'chalk' +import destr from 'destr' import { copy, copyFile, emptyDir, ensureDir, readJSON, readJson, writeJSON, writeJson } from 'fs-extra' import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin' import type { RouteHas } from 'next/dist/lib/load-custom-routes' +import { outdent } from 'outdent' import { getRequiredServerFiles } from './config' @@ -198,14 +201,13 @@ export const writeEdgeFunctions = async (netlifyConfig: NetlifyConfig) => { const edgeFunctionRoot = resolve('.netlify', 'edge-functions') await emptyDir(edgeFunctionRoot) - await copy(getEdgeTemplatePath('../edge-shared'), join(edgeFunctionRoot, 'edge-shared')) - const { publish } = netlifyConfig.build const nextConfigFile = await getRequiredServerFiles(publish) const nextConfig = nextConfigFile.config + await copy(getEdgeTemplatePath('../edge-shared'), join(edgeFunctionRoot, 'edge-shared')) await writeJSON(join(edgeFunctionRoot, 'edge-shared', 'nextConfig.json'), nextConfig) - if (!process.env.NEXT_DISABLE_EDGE_IMAGES) { + if (!destr(process.env.NEXT_DISABLE_EDGE_IMAGES) && !destr(process.env.NEXT_DISABLE_NETLIFY_EDGE)) { console.log( 'Using Netlify Edge Functions for image format detection. Set env var "NEXT_DISABLE_EDGE_IMAGES=true" to disable.', ) @@ -221,14 +223,17 @@ export const writeEdgeFunctions = async (netlifyConfig: NetlifyConfig) => { path: '/_next/image*', }) } - if (process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1') { + if (!destr(process.env.NEXT_DISABLE_NETLIFY_EDGE)) { const middlewareManifest = await loadMiddlewareManifest(netlifyConfig) if (!middlewareManifest) { console.error("Couldn't find the middleware manifest") return } + let usesEdge = false + for (const middleware of middlewareManifest.sortedMiddleware) { + usesEdge = true const edgeFunctionDefinition = middlewareManifest.middleware[middleware] const functionDefinitions = await writeEdgeFunction({ edgeFunctionDefinition, @@ -241,6 +246,7 @@ export const writeEdgeFunctions = async (netlifyConfig: NetlifyConfig) => { // No, the version field was not incremented if (typeof middlewareManifest.functions === 'object') { for (const edgeFunctionDefinition of Object.values(middlewareManifest.functions)) { + usesEdge = true const functionDefinitions = await writeEdgeFunction({ edgeFunctionDefinition, edgeFunctionRoot, @@ -249,6 +255,12 @@ export const writeEdgeFunctions = async (netlifyConfig: NetlifyConfig) => { manifest.functions.push(...functionDefinitions) } } + if (usesEdge) { + console.log(outdent` + ✨ Deploying middleware and functions to ${greenBright`Netlify Edge Functions`} ✨ + This feature is in beta. Please share your feedback here: https://ntl.fyi/next-netlify-edge + `) + } } await writeJson(join(edgeFunctionRoot, 'manifest.json'), manifest) } diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index abf9cc1759..7f53c1386f 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -2,7 +2,8 @@ import { join, relative } from 'path' import type { NetlifyPlugin } from '@netlify/build' -import { greenBright, bold, redBright } from 'chalk' +import { bold, redBright } from 'chalk' +import destr from 'destr' import { existsSync, readFileSync } from 'fs-extra' import { outdent } from 'outdent' @@ -16,12 +17,7 @@ import { generateCustomHeaders, } from './helpers/config' import { onPreDev } from './helpers/dev' -import { - enableEdgeInNextConfig, - writeEdgeFunctions, - loadMiddlewareManifest, - cleanupEdgeFunctions, -} from './helpers/edge' +import { writeEdgeFunctions, loadMiddlewareManifest, cleanupEdgeFunctions } from './helpers/edge' import { moveStaticPages, movePublicFiles, patchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' @@ -88,30 +84,30 @@ const plugin: NetlifyPlugin = { const middlewareManifest = await loadMiddlewareManifest(netlifyConfig) - let usingEdge = false - - if (middlewareManifest?.functions && Object.keys(middlewareManifest.functions).length !== 0) { - usingEdge = true - if (process.env.NEXT_DISABLE_NETLIFY_EDGE === 'true' || process.env.NEXT_DISABLE_NETLIFY_EDGE === '1') { - failBuild(outdent` - You are using Next.js experimental edge runtime, but have set NEXT_DISABLE_NETLIFY_EDGE to true. This is not supported. - To use edge runtime, remove the env var ${bold`NEXT_DISABLE_NETLIFY_EDGE`} or set it to false. - `) - } + if ( + middlewareManifest?.functions && + Object.keys(middlewareManifest.functions).length !== 0 && + destr(process.env.NEXT_DISABLE_NETLIFY_EDGE) + ) { + failBuild(outdent` + You are using Next.js experimental edge runtime, but have set NEXT_DISABLE_NETLIFY_EDGE to true. This is not supported. + To use edge runtime, remove the env var ${bold`NEXT_DISABLE_NETLIFY_EDGE`}. + `) } - if (middlewareManifest?.middleware && Object.keys(middlewareManifest.middleware).length !== 0) { - usingEdge = true - if (process.env.NEXT_DISABLE_NETLIFY_EDGE === 'true' || process.env.NEXT_DISABLE_NETLIFY_EDGE === '1') { - console.log( - redBright(outdent` - You are using Next.js Middleware without Netlify Edge Functions. - This is deprecated because it negatively affects performance and will disable ISR and static rendering. - It also disables advanced middleware features from @netlify/next - To get the best performance and use Netlify Edge Functions, remove the env var ${bold`NEXT_DISABLE_NETLIFY_EDGE`} or set it to false. - `), - ) - } + if ( + middlewareManifest?.middleware && + Object.keys(middlewareManifest.middleware).length !== 0 && + destr(process.env.NEXT_DISABLE_NETLIFY_EDGE) + ) { + console.log( + redBright(outdent` + You are using Next.js Middleware without Netlify Edge Functions. + This is deprecated because it negatively affects performance and will disable ISR and static rendering. + It also disables advanced middleware features from @netlify/next + To get the best performance and use Netlify Edge Functions, remove the env var ${bold`NEXT_DISABLE_NETLIFY_EDGE`}. + `), + ) } if (isNextAuthInstalled()) { @@ -154,7 +150,7 @@ const plugin: NetlifyPlugin = { await patchNextFiles(basePath) - if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) { + if (!destr(process.env.SERVE_STATIC_FILES_FROM_ORIGIN)) { await moveStaticPages({ target, netlifyConfig, i18n, basePath }) } @@ -178,16 +174,7 @@ const plugin: NetlifyPlugin = { buildId, }) - if (usingEdge) { - await writeEdgeFunctions(netlifyConfig) - - await enableEdgeInNextConfig(publish) - - console.log(outdent` - ✨ Deploying middleware and functions to ${greenBright`Netlify Edge Functions`} ✨ - This feature is in beta. Please share your feedback here: https://ntl.fyi/next-netlify-edge - `) - } + await writeEdgeFunctions(netlifyConfig) }, async onPostBuild({ diff --git a/test/index.js b/test/index.js index 32f9bfdcfe..c5eb0b4f54 100644 --- a/test/index.js +++ b/test/index.js @@ -571,6 +571,29 @@ describe('onBuild()', () => { 'X-Foo': mockHeaderValue, }) }) + + test('generates an ipx edge function by default', async () => { + await moveNextDist() + await nextRuntime.onBuild(defaultArgs) + expect(existsSync(path.join('.netlify', 'edge-functions', 'ipx', 'index.ts'))).toBeTruthy() + }) + + test('does not generate an ipx edge function if the feature is disabled', async () => { + process.env.NEXT_DISABLE_EDGE_IMAGES = '1' + await moveNextDist() + await nextRuntime.onBuild(defaultArgs) + expect(existsSync(path.join('.netlify', 'edge-functions', 'ipx', 'index.ts'))).toBeFalsy() + delete process.env.NEXT_DISABLE_EDGE_IMAGES + }) + + test('does not generate an ipx edge function if Netlify Edge is disabled', async () => { + process.env.NEXT_DISABLE_NETLIFY_EDGE = '1' + await moveNextDist() + await nextRuntime.onBuild(defaultArgs) + + expect(existsSync(path.join('.netlify', 'edge-functions', 'ipx', 'index.ts'))).toBeFalsy() + delete process.env.NEXT_DISABLE_NETLIFY_EDGE + }) }) describe('onPostBuild', () => {