@netlify/plugin-nextjs 5.0.0-beta.2 → 5.0.0-beta.4

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.
Files changed (46) hide show
  1. package/README.md +5 -2
  2. package/dist/build/cache.js +4 -4
  3. package/dist/build/content/prerendered.js +5 -5
  4. package/dist/build/content/server.js +5 -5
  5. package/dist/build/content/static.js +5 -5
  6. package/dist/build/functions/edge.js +6 -5
  7. package/dist/build/functions/server.js +7 -7
  8. package/dist/build/image-cdn.js +4 -4
  9. package/dist/build/plugin-context.js +4 -4
  10. package/dist/esm-chunks/{chunk-5SZ5JD6J.js → chunk-6HBXWNZS.js} +27 -9
  11. package/dist/esm-chunks/{chunk-S5JXJCXP.js → chunk-DZM5AUXX.js} +4 -4
  12. package/dist/esm-chunks/{chunk-H46DW7YI.js → chunk-EPSI5TTB.js} +2 -2
  13. package/dist/esm-chunks/{chunk-TJKO3X6O.js → chunk-IZY5TPZE.js} +27 -3
  14. package/dist/esm-chunks/{chunk-4AJYXTWN.js → chunk-K233JI4O.js} +2 -2
  15. package/dist/esm-chunks/{chunk-Z7ZMLVTM.js → chunk-KMPIVUVT.js} +4 -4
  16. package/dist/esm-chunks/{chunk-AVWFCGVE.js → chunk-MFN4GH7U.js} +3 -3
  17. package/dist/esm-chunks/{chunk-B6QMRLBH.js → chunk-NE4HYI2D.js} +3 -3
  18. package/dist/esm-chunks/{chunk-RSKIKBZH.js → chunk-WELZ7LFO.js} +2 -2
  19. package/dist/esm-chunks/{chunk-ALO2SSMH.js → chunk-X4Q4MYBW.js} +39 -28
  20. package/dist/esm-chunks/chunk-X7XIMV6B.js +105 -0
  21. package/dist/esm-chunks/{chunk-GGHAQM5D.js → chunk-XA2CZH5Y.js} +7 -3
  22. package/dist/esm-chunks/{chunk-3PTPU5GO.js → chunk-XC7T747I.js} +48 -15
  23. package/dist/esm-chunks/chunk-XIP2W57K.js +39 -0
  24. package/dist/esm-chunks/{chunk-SMSOJ2OS.js → chunk-YG3G7B2X.js} +2 -2
  25. package/dist/index.js +18 -14
  26. package/dist/run/config.js +5 -5
  27. package/dist/run/constants.js +4 -4
  28. package/dist/run/handlers/server.js +16 -14
  29. package/dist/run/headers.js +4 -4
  30. package/dist/run/revalidate.js +4 -4
  31. package/dist/run/systemlog.js +9 -8
  32. package/edge-runtime/lib/headers.ts +5 -0
  33. package/edge-runtime/lib/logging.ts +5 -0
  34. package/edge-runtime/lib/next-request.ts +43 -1
  35. package/edge-runtime/lib/response.ts +81 -19
  36. package/edge-runtime/lib/util.test.ts +39 -0
  37. package/edge-runtime/lib/util.ts +79 -7
  38. package/edge-runtime/middleware.ts +19 -2
  39. package/edge-runtime/next.config.json +1 -0
  40. package/edge-runtime/vendor/import_map.json +2 -1
  41. package/edge-runtime/vendor/v1-7-0--edge-utils.netlify.app/logger/logger.ts +164 -0
  42. package/edge-runtime/vendor/v1-7-0--edge-utils.netlify.app/logger/mod.ts +1 -0
  43. package/edge-runtime/vendor.ts +2 -0
  44. package/package.json +1 -1
  45. package/dist/esm-chunks/chunk-R4NHZWGU.js +0 -32
  46. package/dist/esm-chunks/chunk-YZXA5QBC.js +0 -60
@@ -0,0 +1,39 @@
1
+ import { assertEquals } from 'https://deno.land/std@0.175.0/testing/asserts.ts'
2
+ import { rewriteDataPath } from './util.ts'
3
+
4
+ Deno.test('rewriteDataPath', async (t) => {
5
+ await t.step('should rewrite a data url', async () => {
6
+ const dataUrl = '/_next/data/build-id/rewrite-me.json'
7
+ const newRoute = '/target'
8
+ const result = rewriteDataPath({ dataUrl, newRoute })
9
+ assertEquals(result, '/_next/data/build-id/target.json')
10
+ })
11
+
12
+ await t.step('should rewrite a data url with a base path', async () => {
13
+ const dataUrl = '/baseDir/_next/data/build-id/rewrite-me.json'
14
+ const newRoute = '/target'
15
+ const result = rewriteDataPath({ dataUrl, newRoute, basePath: '/baseDir' })
16
+ assertEquals(result, '/baseDir/_next/data/build-id/target.json')
17
+ })
18
+
19
+ await t.step('should rewrite from an index data url', async () => {
20
+ const dataUrl = '/_next/data/build-id/index.json'
21
+ const newRoute = '/target'
22
+ const result = rewriteDataPath({ dataUrl, newRoute })
23
+ assertEquals(result, '/_next/data/build-id/target.json')
24
+ })
25
+
26
+ await t.step('should rewrite to an index data url', async () => {
27
+ const dataUrl = '/_next/data/build-id/rewrite-me.json'
28
+ const newRoute = '/'
29
+ const result = rewriteDataPath({ dataUrl, newRoute })
30
+ assertEquals(result, '/_next/data/build-id/index.json')
31
+ })
32
+
33
+ await t.step('should rewrite to a route with a trailing slash', async () => {
34
+ const dataUrl = '/_next/data/build-id/rewrite-me.json'
35
+ const newRoute = '/target/'
36
+ const result = rewriteDataPath({ dataUrl, newRoute })
37
+ assertEquals(result, '/_next/data/build-id/target.json')
38
+ })
39
+ })
@@ -1,16 +1,63 @@
1
- // If the redirect is a data URL, we need to normalize it.
2
- // https://github.com/vercel/next.js/blob/25e0988e7c9033cb1503cbe0c62ba5de2e97849c/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L69-L76
3
- export function normalizeDataUrl(redirect: string) {
4
- if (redirect.startsWith('/_next/data/') && redirect.includes('.json')) {
5
- const paths = redirect
1
+ import type { RequestData } from './next-request.ts'
2
+
3
+ /**
4
+ * Normalize a data URL into a route path.
5
+ * @see https://github.com/vercel/next.js/blob/25e0988e7c9033cb1503cbe0c62ba5de2e97849c/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L69-L76
6
+ */
7
+ export function normalizeDataUrl(urlPath: string) {
8
+ if (urlPath.startsWith('/_next/data/') && urlPath.includes('.json')) {
9
+ const paths = urlPath
6
10
  .replace(/^\/_next\/data\//, '')
7
11
  .replace(/\.json/, '')
8
12
  .split('/')
9
13
 
10
- redirect = paths[1] !== 'index' ? `/${paths.slice(1).join('/')}` : '/'
14
+ urlPath = paths[1] !== 'index' ? `/${paths.slice(1).join('/')}` : '/'
11
15
  }
12
16
 
13
- return redirect
17
+ return urlPath
18
+ }
19
+
20
+ export const removeBasePath = (path: string, basePath?: string) => {
21
+ if (basePath && path.startsWith(basePath)) {
22
+ return path.replace(basePath, '')
23
+ }
24
+ return path
25
+ }
26
+
27
+ // https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/i18n/normalize-locale-path.ts
28
+
29
+ export interface PathLocale {
30
+ detectedLocale?: string
31
+ pathname: string
32
+ }
33
+
34
+ /**
35
+ * For a pathname that may include a locale from a list of locales, it
36
+ * removes the locale from the pathname returning it alongside with the
37
+ * detected locale.
38
+ *
39
+ * @param pathname A pathname that may include a locale.
40
+ * @param locales A list of locales.
41
+ * @returns The detected locale and pathname without locale
42
+ */
43
+ export function normalizeLocalePath(pathname: string, locales?: string[]): PathLocale {
44
+ let detectedLocale: string | undefined
45
+ // first item will be empty string from splitting at first char
46
+ const pathnameParts = pathname.split('/')
47
+
48
+ ;(locales || []).some((locale) => {
49
+ if (pathnameParts[1] && pathnameParts[1].toLowerCase() === locale.toLowerCase()) {
50
+ detectedLocale = locale
51
+ pathnameParts.splice(1, 1)
52
+ pathname = pathnameParts.join('/') || '/'
53
+ return true
54
+ }
55
+ return false
56
+ })
57
+ return {
58
+ pathname,
59
+ detectedLocale,
60
+ }
14
61
  }
15
62
 
16
63
  /**
@@ -24,3 +71,28 @@ export function relativizeURL(url: string | string, base: string | URL) {
24
71
  ? relative.toString().replace(origin, '')
25
72
  : relative.toString()
26
73
  }
74
+
75
+ export const normalizeIndex = (path: string) => (path === '/' ? '/index' : path)
76
+
77
+ const stripTrailingSlash = (path: string) =>
78
+ path !== '/' && path.endsWith('/') ? path.slice(0, -1) : path
79
+
80
+ /**
81
+ * Modify a data url to point to a new page route.
82
+ */
83
+ export function rewriteDataPath({
84
+ dataUrl,
85
+ newRoute,
86
+ basePath,
87
+ }: {
88
+ dataUrl: string
89
+ newRoute: string
90
+ basePath?: string
91
+ }) {
92
+ const normalizedDataUrl = normalizeDataUrl(removeBasePath(dataUrl, basePath))
93
+
94
+ return dataUrl.replace(
95
+ normalizeIndex(normalizedDataUrl),
96
+ stripTrailingSlash(normalizeIndex(newRoute)),
97
+ )
98
+ }
@@ -1,7 +1,10 @@
1
1
  import type { Context } from '@netlify/edge-functions'
2
2
 
3
3
  import matchers from './matchers.json' assert { type: 'json' }
4
+ import nextConfig from './next.config.json' assert { type: 'json' }
4
5
 
6
+ import { InternalHeaders } from './lib/headers.ts'
7
+ import { logger, LogLevel } from './lib/logging.ts'
5
8
  import { buildNextRequest, RequestData } from './lib/next-request.ts'
6
9
  import { buildResponse } from './lib/response.ts'
7
10
  import { FetchEventResult } from './lib/response.ts'
@@ -29,20 +32,34 @@ export async function handleMiddleware(
29
32
  context: Context,
30
33
  nextHandler: NextHandler,
31
34
  ) {
32
- const nextRequest = buildNextRequest(request, context)
35
+ const nextRequest = buildNextRequest(request, context, nextConfig)
33
36
  const url = new URL(request.url)
37
+ const reqLogger = logger
38
+ .withLogLevel(
39
+ request.headers.has(InternalHeaders.NFDebugLogging) ? LogLevel.Debug : LogLevel.Log,
40
+ )
41
+ .withFields({ url_path: url.pathname })
42
+ .withRequestID(request.headers.get(InternalHeaders.NFRequestID))
34
43
 
35
44
  // While we have already checked the path when mapping to the edge function,
36
45
  // Next.js supports extra rules that we need to check here too, because we
37
46
  // might be running an edge function for a path we should not. If we find
38
47
  // that's the case, short-circuit the execution.
39
48
  if (!matchesMiddleware(url.pathname, request, searchParamsToUrlQuery(url.searchParams))) {
49
+ reqLogger.debug('Aborting middleware due to runtime rules')
50
+
40
51
  return
41
52
  }
42
53
 
43
54
  try {
44
55
  const result = await nextHandler({ request: nextRequest })
45
- const response = await buildResponse({ result, request: request, context })
56
+ const response = await buildResponse({
57
+ context,
58
+ logger: reqLogger,
59
+ request,
60
+ result,
61
+ requestLocale: nextRequest.detectedLocale,
62
+ })
46
63
 
47
64
  return response
48
65
  } catch (error) {
@@ -0,0 +1 @@
1
+ {}
@@ -3,7 +3,8 @@
3
3
  "https://ghuc.cc/worker-tools/resolvable-promise/index.ts": "./raw.githubusercontent.com/worker-tools/resolvable-promise/master/index.ts",
4
4
  "https://deno.land/": "./deno.land/",
5
5
  "https://esm.sh/": "./esm.sh/",
6
- "https://raw.githubusercontent.com/": "./raw.githubusercontent.com/"
6
+ "https://raw.githubusercontent.com/": "./raw.githubusercontent.com/",
7
+ "https://v1-7-0--edge-utils.netlify.app/": "./v1-7-0--edge-utils.netlify.app/"
7
8
  },
8
9
  "scopes": {
9
10
  "./esm.sh/": {
@@ -0,0 +1,164 @@
1
+ type Logger = (...data: unknown[]) => void;
2
+
3
+ export enum LogLevel {
4
+ Debug = 1,
5
+ Log,
6
+ Error,
7
+ }
8
+
9
+ const serializeError = (error: Error): Record<string, unknown> => {
10
+ const cause = error?.cause instanceof Error
11
+ ? serializeError(error.cause)
12
+ : error.cause;
13
+
14
+ return {
15
+ error: error.message,
16
+ error_cause: cause,
17
+ error_stack: error.stack,
18
+ };
19
+ };
20
+
21
+ export class StructuredLogger {
22
+ private fields: Record<string, unknown>;
23
+ private logLevel: LogLevel;
24
+ private message: string;
25
+ private rawLogger?: Logger;
26
+ private requestID?: string;
27
+ __netlifyStructuredLogger: number;
28
+
29
+ constructor(
30
+ message?: string,
31
+ fields?: Record<string, unknown>,
32
+ requestID?: string,
33
+ rawLogger?: Logger,
34
+ logLevel?: LogLevel,
35
+ ) {
36
+ this.fields = fields ?? {};
37
+ this.logLevel = logLevel ?? LogLevel.Log;
38
+ this.message = message ?? "";
39
+ this.rawLogger = rawLogger;
40
+ this.requestID = requestID;
41
+
42
+ // Value should be equal to the major version
43
+ this.__netlifyStructuredLogger = 1;
44
+ }
45
+
46
+ debug(message: string) {
47
+ if (this.logLevel > LogLevel.Debug) {
48
+ return;
49
+ }
50
+
51
+ const logger = this.rawLogger ?? globalThis.console.log;
52
+
53
+ logger(
54
+ new StructuredLogger(
55
+ message,
56
+ this.fields,
57
+ this.requestID,
58
+ this.rawLogger,
59
+ this.logLevel,
60
+ ),
61
+ );
62
+ }
63
+
64
+ error(message: string) {
65
+ if (this.logLevel > LogLevel.Error) {
66
+ return;
67
+ }
68
+
69
+ const logger = this.rawLogger ?? globalThis.console.log;
70
+
71
+ logger(
72
+ new StructuredLogger(
73
+ message,
74
+ this.fields,
75
+ this.requestID,
76
+ this.rawLogger,
77
+ this.logLevel,
78
+ ),
79
+ );
80
+ }
81
+
82
+ log(message: string) {
83
+ if (this.logLevel > LogLevel.Log) {
84
+ return;
85
+ }
86
+
87
+ const logger = this.rawLogger ?? globalThis.console.log;
88
+
89
+ logger(
90
+ new StructuredLogger(
91
+ message,
92
+ this.fields,
93
+ this.requestID,
94
+ this.rawLogger,
95
+ this.logLevel,
96
+ ),
97
+ );
98
+ }
99
+
100
+ serialize() {
101
+ const log = {
102
+ fields: this.fields,
103
+ message: this.message,
104
+ requestID: this.requestID,
105
+ };
106
+
107
+ return log;
108
+ }
109
+
110
+ withError(error: unknown) {
111
+ const fields = error instanceof Error ? serializeError(error) : { error };
112
+
113
+ return this.withFields(fields);
114
+ }
115
+
116
+ withFields(fields: Record<string, unknown>) {
117
+ return new StructuredLogger(
118
+ this.message,
119
+ {
120
+ ...this.fields,
121
+ ...fields,
122
+ },
123
+ this.requestID,
124
+ this.rawLogger,
125
+ this.logLevel,
126
+ );
127
+ }
128
+
129
+ withLogLevel(logLevel: LogLevel) {
130
+ return new StructuredLogger(
131
+ this.message,
132
+ this.fields,
133
+ this.requestID,
134
+ this.rawLogger,
135
+ logLevel,
136
+ );
137
+ }
138
+
139
+ withRawLogger(logger: Logger) {
140
+ return new StructuredLogger(
141
+ this.message,
142
+ this.fields,
143
+ this.requestID,
144
+ logger,
145
+ this.logLevel,
146
+ );
147
+ }
148
+
149
+ withRequestID(requestID: string | null) {
150
+ if (requestID === null) {
151
+ return this;
152
+ }
153
+
154
+ return new StructuredLogger(
155
+ this.message,
156
+ this.fields,
157
+ requestID,
158
+ this.rawLogger,
159
+ this.logLevel,
160
+ );
161
+ }
162
+ }
163
+
164
+ export const logger = new StructuredLogger();
@@ -0,0 +1 @@
1
+ export * from "./logger.ts";
@@ -16,3 +16,5 @@ import 'https://deno.land/x/html_rewriter@v0.1.0-pre.17/index.ts'
16
16
 
17
17
  import 'https://esm.sh/v91/next@12.2.5/deno/dist/server/web/spec-extension/request.js'
18
18
  import 'https://esm.sh/v91/next@12.2.5/deno/dist/server/web/spec-extension/response.js'
19
+
20
+ import 'https://v1-7-0--edge-utils.netlify.app/logger/mod.ts'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.0.0-beta.2",
3
+ "version": "5.0.0-beta.4",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -1,32 +0,0 @@
1
-
2
- const require = await (async () => {
3
- const { createRequire } = await import("node:module");
4
- return createRequire(import.meta.url);
5
- })();
6
-
7
- import {
8
- PLUGIN_DIR
9
- } from "./chunk-4AJYXTWN.js";
10
-
11
- // src/run/config.ts
12
- import { readFile } from "node:fs/promises";
13
- import { resolve } from "node:path";
14
- var getRunConfig = async () => {
15
- return JSON.parse(await readFile(resolve(".next/required-server-files.json"), "utf-8")).config;
16
- };
17
- var setRunConfig = (config) => {
18
- config.experimental = {
19
- ...config.experimental,
20
- incrementalCacheHandlerPath: `${PLUGIN_DIR}/dist/run/handlers/cache.cjs`
21
- };
22
- process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(config);
23
- };
24
- var getTagsManifest = async () => {
25
- return JSON.parse(await readFile(resolve(".netlify/tags-manifest.json"), "utf-8"));
26
- };
27
-
28
- export {
29
- getRunConfig,
30
- setRunConfig,
31
- getTagsManifest
32
- };
@@ -1,60 +0,0 @@
1
-
2
- const require = await (async () => {
3
- const { createRequire } = await import("node:module");
4
- return createRequire(import.meta.url);
5
- })();
6
-
7
-
8
- // src/run/systemlog.ts
9
- var systemLogTag = "__nfSystemLog";
10
- var serializeError = (error) => {
11
- const cause = error?.cause instanceof Error ? serializeError(error.cause) : error.cause;
12
- return {
13
- error: error.message,
14
- error_cause: cause,
15
- error_stack: error.stack
16
- };
17
- };
18
- var StructuredLogger = class _StructuredLogger {
19
- fields;
20
- message;
21
- constructor(message, fields) {
22
- this.fields = fields ?? {};
23
- this.message = message ?? "";
24
- }
25
- // TODO: add sampling
26
- doLog(logger2, message) {
27
- logger2(systemLogTag, JSON.stringify({ msg: message, fields: this.fields }));
28
- }
29
- log(message) {
30
- this.doLog(console.log, message);
31
- }
32
- info(message) {
33
- this.doLog(console.info, message);
34
- }
35
- debug(message) {
36
- this.doLog(console.debug, message);
37
- }
38
- warn(message) {
39
- this.doLog(console.warn, message);
40
- }
41
- error(message) {
42
- this.doLog(console.error, message);
43
- }
44
- withError(error) {
45
- const fields = error instanceof Error ? serializeError(error) : { error };
46
- return this.withFields(fields);
47
- }
48
- withFields(fields) {
49
- return new _StructuredLogger(this.message, {
50
- ...this.fields,
51
- ...fields
52
- });
53
- }
54
- };
55
- var logger = new StructuredLogger();
56
-
57
- export {
58
- StructuredLogger,
59
- logger
60
- };