@netlify/plugin-nextjs 4.29.4 → 4.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -364,7 +364,7 @@ const unpatchNextFiles = async (root) => {
364
364
  }
365
365
  };
366
366
  exports.unpatchNextFiles = unpatchNextFiles;
367
- const movePublicFiles = async ({ appDir, outdir, publish, }) => {
367
+ const movePublicFiles = async ({ appDir, outdir, publish, basePath, }) => {
368
368
  // `outdir` is a config property added when using Next.js with Nx. It's typically
369
369
  // a relative path outside of the appDir, e.g. '../../dist/apps/<app-name>', and
370
370
  // the parent directory of the .next directory.
@@ -373,7 +373,7 @@ const movePublicFiles = async ({ appDir, outdir, publish, }) => {
373
373
  // directory from the original app directory.
374
374
  const publicDir = outdir ? (0, pathe_1.join)(appDir, outdir, 'public') : (0, pathe_1.join)(appDir, 'public');
375
375
  if ((0, fs_extra_1.existsSync)(publicDir)) {
376
- await (0, fs_extra_1.copy)(publicDir, `${publish}/`);
376
+ await (0, fs_extra_1.copy)(publicDir, `${publish}${basePath}/`);
377
377
  }
378
378
  };
379
379
  exports.movePublicFiles = movePublicFiles;
package/lib/index.js CHANGED
@@ -94,7 +94,7 @@ const plugin = {
94
94
  const apiRoutes = await (0, functions_1.getExtendedApiRouteConfigs)(publish, appDir);
95
95
  await (0, functions_1.generateFunctions)(constants, appDir, apiRoutes);
96
96
  await (0, functions_1.generatePagesResolver)(constants);
97
- await (0, files_1.movePublicFiles)({ appDir, outdir, publish });
97
+ await (0, files_1.movePublicFiles)({ appDir, outdir, publish, basePath });
98
98
  await (0, files_1.patchNextFiles)(appDir);
99
99
  if (!(0, destr_1.default)(process.env.SERVE_STATIC_FILES_FROM_ORIGIN)) {
100
100
  await (0, files_1.moveStaticPages)({ target, netlifyConfig, i18n, basePath });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.29.4",
3
+ "version": "4.30.0",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -12,8 +12,8 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "@netlify/esbuild": "0.14.39",
15
- "@netlify/functions": "^1.3.0",
16
- "@netlify/ipx": "^1.3.2",
15
+ "@netlify/functions": "^1.4.0",
16
+ "@netlify/ipx": "^1.3.3",
17
17
  "@vercel/node-bridge": "^2.1.0",
18
18
  "chalk": "^4.1.2",
19
19
  "destr": "^1.1.1",
@@ -36,12 +36,12 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@delucis/if-env": "^1.1.2",
39
- "@netlify/build": "^29.1.3",
39
+ "@netlify/build": "^29.4.1",
40
40
  "@types/fs-extra": "^9.0.13",
41
41
  "@types/jest": "^27.4.1",
42
42
  "@types/merge-stream": "^1.1.2",
43
43
  "@types/node": "^17.0.25",
44
- "next": "^13.0.6",
44
+ "next": "^13.0.7",
45
45
  "npm-run-all": "^4.1.5",
46
46
  "typescript": "^4.6.3"
47
47
  },
@@ -0,0 +1,64 @@
1
+ import { assertEquals } from 'https://deno.land/std@0.167.0/testing/asserts.ts'
2
+ import { beforeEach, describe, it } from 'https://deno.land/std@0.167.0/testing/bdd.ts'
3
+ import { updateModifiedHeaders, FetchEventResult } from './utils.ts'
4
+
5
+ describe('updateModifiedHeaders', () => {
6
+ it("does not modify the headers if 'x-middleware-override-headers' is not found", () => {
7
+ const mockHeaders = new Headers()
8
+ // There shouldn't be a case where x-middleware-override-headers is not set and a header has
9
+ // been modified with 'x-middleware-request' added to it, this is more to confirm the test case
10
+ mockHeaders.set('x-middleware-request-foo', 'bar')
11
+
12
+ const mockResponse = {
13
+ headers: mockHeaders,
14
+ }
15
+
16
+ const mockRequest = {
17
+ headers: new Headers(),
18
+ }
19
+
20
+ updateModifiedHeaders(mockRequest.headers, mockResponse.headers)
21
+
22
+ assertEquals(mockRequest.headers.get('x-middleware-request-foo'), null)
23
+ })
24
+
25
+ describe("when the 'x-middleware-override-headers' headers is present", () => {
26
+ let mockHeaders
27
+ let mockRequest: { headers: Headers }
28
+ let mockResponse: { headers: Headers }
29
+
30
+ beforeEach(() => {
31
+ mockHeaders = new Headers()
32
+ mockHeaders.set('foo', 'bar')
33
+ mockHeaders.set('x-middleware-request-hello', 'world')
34
+ mockHeaders.set('x-middleware-request-test', '123')
35
+ mockHeaders.set('x-middleware-override-headers', 'hello,test')
36
+
37
+ mockRequest = {
38
+ headers: new Headers(),
39
+ }
40
+
41
+ mockResponse = {
42
+ headers: mockHeaders,
43
+ }
44
+
45
+ updateModifiedHeaders(mockRequest.headers, mockResponse.headers)
46
+ })
47
+
48
+ it("does not modify or add headers that are missing 'x-middleware-request' in the name", () => {
49
+ assertEquals(mockRequest.headers.get('foo'), null)
50
+ })
51
+
52
+ it("removes 'x-middleware-request-' from headers", () => {
53
+ assertEquals(mockRequest.headers.get('x-middleware-request-hello'), null)
54
+ assertEquals(mockRequest.headers.get('x-middleware-request-test'), null)
55
+
56
+ assertEquals(mockRequest.headers.get('hello'), 'world')
57
+ assertEquals(mockRequest.headers.get('test'), '123')
58
+ })
59
+
60
+ it("removes 'x-middleware-override-headers' after cleaning headers", () => {
61
+ assertEquals(mockRequest.headers.get('x-middleware-override-headers'), null)
62
+ })
63
+ })
64
+ })
@@ -31,8 +31,13 @@ export const addMiddlewareHeaders = async (
31
31
  // We need to await the response to get the origin headers, then we can add the ones from middleware.
32
32
  const res = await originResponse
33
33
  const response = new Response(res.body, res)
34
+ const originCookies = response.headers.get('set-cookie')
34
35
  middlewareResponse.headers.forEach((value, key) => {
35
36
  response.headers.set(key, value)
37
+ // Append origin cookies after middleware cookies
38
+ if (key === 'set-cookie' && originCookies) {
39
+ response.headers.append(key, originCookies)
40
+ }
36
41
  })
37
42
  return response
38
43
  }
@@ -59,6 +64,32 @@ function isMiddlewareResponse(response: Response | MiddlewareResponse): response
59
64
  return 'dataTransforms' in response
60
65
  }
61
66
 
67
+ // Next 13 supports request header mutations and has the side effect of prepending header values with 'x-middleware-request'
68
+ // as part of invoking NextResponse.next() in the middleware. We need to remove that before sending the response the user
69
+ // as the code that removes it in Next isn't run based on how we handle the middleware
70
+ //
71
+ // Related Next.js code:
72
+ // * https://github.com/vercel/next.js/blob/68d06fe015b28d8f81da52ca107a5f4bd72ab37c/packages/next/server/next-server.ts#L1918-L1928
73
+ // * https://github.com/vercel/next.js/blob/43c9d8940dc42337dd2f7d66aa90e6abf952278e/packages/next/server/web/spec-extension/response.ts#L10-L27
74
+ export function updateModifiedHeaders(requestHeaders: Headers, responseHeaders: Headers) {
75
+ const overriddenHeaders = responseHeaders.get('x-middleware-override-headers')
76
+
77
+ if (!overriddenHeaders) {
78
+ return
79
+ }
80
+
81
+ const headersToUpdate = overriddenHeaders.split(',').map((header) => header.trim())
82
+
83
+ for (const header of headersToUpdate) {
84
+ const oldHeaderKey = 'x-middleware-request-' + header
85
+ const headerValue = responseHeaders.get(oldHeaderKey) || ''
86
+
87
+ requestHeaders.set(header, headerValue)
88
+ responseHeaders.delete(oldHeaderKey)
89
+ }
90
+ responseHeaders.delete('x-middleware-override-headers')
91
+ }
92
+
62
93
  export const buildResponse = async ({
63
94
  result,
64
95
  request,
@@ -68,6 +99,8 @@ export const buildResponse = async ({
68
99
  request: Request
69
100
  context: Context
70
101
  }) => {
102
+ updateModifiedHeaders(request.headers, result.response.headers)
103
+
71
104
  // They've returned the MiddlewareRequest directly, so we'll call `next()` for them.
72
105
  if (isMiddlewareRequest(result.response)) {
73
106
  result.response = await result.response.next()