@netlify/plugin-nextjs 4.30.2 → 4.30.3

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.
@@ -87,6 +87,7 @@ const IMPORT_UNSUPPORTED = [
87
87
  const getMiddlewareBundle = async ({ edgeFunctionDefinition, netlifyConfig, }) => {
88
88
  const { publish } = netlifyConfig.build;
89
89
  const chunks = [preamble];
90
+ chunks.push(`export const _DEFINITION = ${JSON.stringify(edgeFunctionDefinition)}`);
90
91
  if ('wasm' in edgeFunctionDefinition) {
91
92
  for (const { name, filePath } of edgeFunctionDefinition.wasm) {
92
93
  const wasm = await fs_1.promises.readFile((0, path_1.join)(publish, filePath));
@@ -289,13 +290,17 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
289
290
  });
290
291
  manifest.functions.push(...matchers.map((matcher) => middlewareMatcherToEdgeFunctionDefinition(matcher, functionName)));
291
292
  }
293
+ // Functions (i.e. not middleware, but edge SSR and API routes)
292
294
  if (typeof middlewareManifest.functions === 'object') {
293
295
  // When using the app dir, we also need to check if the EF matches a page
294
296
  const appPathRoutesManifest = await (0, exports.loadAppPathRoutesManifest)(netlifyConfig);
297
+ // A map of all route pages to their page regex. This is used for pages dir and appDir.
295
298
  const pageRegexMap = new Map([...(routesManifest.dynamicRoutes || []), ...(routesManifest.staticRoutes || [])].map((route) => [
296
299
  route.page,
297
300
  route.regex,
298
301
  ]));
302
+ // Create a map of pages-dir routes to their data route regex (appDir uses the same route as the HTML)
303
+ const dataRoutesMap = new Map([...(routesManifest.dataRoutes || [])].map((route) => [route.page, route.dataRouteRegex]));
299
304
  for (const edgeFunctionDefinition of Object.values(middlewareManifest.functions)) {
300
305
  usesEdge = true;
301
306
  const functionName = sanitizeName(edgeFunctionDefinition.name);
@@ -317,6 +322,16 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
317
322
  // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
318
323
  cache: usesAppDir ? 'manual' : undefined,
319
324
  });
325
+ // pages-dir page routes also have a data route. If there's a match, add an entry mapping that to the function too
326
+ const dataRoute = dataRoutesMap.get(edgeFunctionDefinition.page);
327
+ if (dataRoute) {
328
+ manifest.functions.push({
329
+ function: functionName,
330
+ name: edgeFunctionDefinition.name,
331
+ pattern: dataRoute,
332
+ cache: usesAppDir ? 'manual' : undefined,
333
+ });
334
+ }
320
335
  }
321
336
  }
322
337
  if (usesEdge) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.30.2",
3
+ "version": "4.30.3",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * This placeholder is replaced with the compiled Next.js bundle at build time
3
3
  * @param {Object} props
4
- * @param {import("./runtime.ts").RequestData} props.request
4
+ * @param {import("./function-runtime.ts").RequestData} props.request
5
5
  * @returns {Promise<import("../edge-shared/utils.ts").FetchEventResult>}
6
6
  */
7
7
  export default async ({ request }) => {}
8
+ export const _DEFINITION = { env: [], files: [], page: 'placeholder', name: 'pages_placeholder', matchers: [] }
@@ -1,6 +1,6 @@
1
1
  import type { Context } from 'https://edge.netlify.com'
2
2
  // Available at build time
3
- import edgeFunction from './bundle.js'
3
+ import { _DEFINITION as edgeFunctionDefinition, default as edgeFunction } from './bundle.js'
4
4
  import { buildNextRequest, buildResponse, redirectTrailingSlash } from '../edge-shared/utils.ts'
5
5
  import nextConfig from '../edge-shared/nextConfig.json' assert { type: 'json' }
6
6
 
@@ -11,6 +11,7 @@ const handler = async (req: Request, context: Context) => {
11
11
  return redirect
12
12
  }
13
13
  const request = buildNextRequest(req, context, nextConfig)
14
+ request.headers['x-matched-path'] ||= edgeFunctionDefinition.page
14
15
  try {
15
16
  const result = await edgeFunction({ request })
16
17
  return buildResponse({ result, request: req, context })
@@ -42,6 +42,7 @@ export type Rewrite = {
42
42
  basePath?: false
43
43
  locale?: false
44
44
  has?: RouteHas[]
45
+ missing?: RouteHas[]
45
46
  regex: string
46
47
  }
47
48
 
@@ -51,6 +52,7 @@ export type Header = {
51
52
  locale?: false
52
53
  headers: Array<{ key: string; value: string }>
53
54
  has?: RouteHas[]
55
+ missing?: RouteHas[]
54
56
  regex: string
55
57
  }
56
58
  export type Redirect = {
@@ -59,6 +61,7 @@ export type Redirect = {
59
61
  basePath?: false
60
62
  locale?: false
61
63
  has?: RouteHas[]
64
+ missing?: RouteHas[]
62
65
  statusCode?: number
63
66
  permanent?: boolean
64
67
  regex: string
@@ -138,11 +141,16 @@ export function parseUrl(url: string): ParsedUrl {
138
141
 
139
142
  // prepare-destination.ts
140
143
  // Changed to use WHATWG Fetch Request instead of IncomingMessage
141
- export function matchHas(req: Pick<Request, 'headers' | 'url'>, has: RouteHas[], query: Params): false | Params {
144
+ export function matchHas(
145
+ req: Pick<Request, 'headers' | 'url'>,
146
+ query: Params,
147
+ has: RouteHas[] = [],
148
+ missing: RouteHas[] = [],
149
+ ): false | Params {
142
150
  const params: Params = {}
143
151
  const cookies = getCookies(req.headers)
144
152
  const url = new URL(req.url)
145
- const allMatch = has.every((hasItem) => {
153
+ const hasMatch = (hasItem: RouteHas) => {
146
154
  let value: undefined | string | null
147
155
  let key = hasItem.key
148
156
 
@@ -189,7 +197,9 @@ export function matchHas(req: Pick<Request, 'headers' | 'url'>, has: RouteHas[],
189
197
  }
190
198
  }
191
199
  return false
192
- })
200
+ }
201
+
202
+ const allMatch = has.every((item) => hasMatch(item)) && !missing.some((item) => hasMatch(item))
193
203
 
194
204
  if (allMatch) {
195
205
  return params
@@ -371,6 +381,7 @@ export interface MiddlewareMatcher {
371
381
  regexp: string
372
382
  locale?: false
373
383
  has?: RouteHas[]
384
+ missing?: RouteHas[]
374
385
  }
375
386
 
376
387
  export function getMiddlewareRouteMatcher(matchers: MiddlewareMatcher[]): MiddlewareRouteMatch {
@@ -381,8 +392,8 @@ export function getMiddlewareRouteMatcher(matchers: MiddlewareMatcher[]): Middle
381
392
  continue
382
393
  }
383
394
 
384
- if (matcher.has) {
385
- const hasParams = matchHas(req, matcher.has, query)
395
+ if (matcher.has || matcher.missing) {
396
+ const hasParams = matchHas(req, query, matcher.has, matcher.missing)
386
397
  if (!hasParams) {
387
398
  continue
388
399
  }