@opennextjs/aws 3.2.2 → 3.3.1

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 (70) hide show
  1. package/README.md +6 -1
  2. package/dist/adapters/cache.d.ts +1 -2
  3. package/dist/adapters/cache.js +2 -12
  4. package/dist/adapters/config/index.d.ts +2 -0
  5. package/dist/adapters/config/index.js +4 -3
  6. package/dist/adapters/config/util.d.ts +2 -0
  7. package/dist/adapters/config/util.js +12 -2
  8. package/dist/adapters/image-optimization-adapter.d.ts +1 -2
  9. package/dist/adapters/logger.js +32 -11
  10. package/dist/adapters/middleware.js +13 -2
  11. package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +1 -0
  12. package/dist/adapters/plugins/image-optimization/image-optimization.js +0 -1
  13. package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +1 -0
  14. package/dist/adapters/util.js +5 -1
  15. package/dist/build/bundleNextServer.d.ts +2 -2
  16. package/dist/build/bundleNextServer.js +2 -2
  17. package/dist/build/copyTracedFiles.js +1 -1
  18. package/dist/build/createAssets.js +23 -24
  19. package/dist/build/createMiddleware.js +1 -1
  20. package/dist/build/createServerBundle.js +3 -3
  21. package/dist/build/edge/createEdgeBundle.d.ts +5 -4
  22. package/dist/build/edge/createEdgeBundle.js +35 -33
  23. package/dist/build/helper.d.ts +6 -1
  24. package/dist/build/helper.js +20 -5
  25. package/dist/build/validateConfig.js +3 -1
  26. package/dist/core/requestHandler.d.ts +1 -2
  27. package/dist/core/requestHandler.js +31 -20
  28. package/dist/core/routing/matcher.js +1 -1
  29. package/dist/core/routing/middleware.d.ts +3 -3
  30. package/dist/core/routing/middleware.js +3 -4
  31. package/dist/core/routing/routeMatcher.d.ts +10 -0
  32. package/dist/core/routing/routeMatcher.js +45 -0
  33. package/dist/core/routing/util.d.ts +3 -3
  34. package/dist/core/routing/util.js +7 -5
  35. package/dist/core/routingHandler.d.ts +5 -8
  36. package/dist/core/routingHandler.js +38 -37
  37. package/dist/helpers/withCloudflare.d.ts +1 -1
  38. package/dist/helpers/withCloudflare.js +2 -2
  39. package/dist/http/openNextResponse.d.ts +2 -10
  40. package/dist/http/openNextResponse.js +5 -3
  41. package/dist/overrides/converters/aws-apigw-v2.js +2 -1
  42. package/dist/overrides/converters/aws-cloudfront.js +1 -1
  43. package/dist/overrides/converters/edge.d.ts +2 -5
  44. package/dist/overrides/converters/edge.js +5 -4
  45. package/dist/overrides/incrementalCache/dummy.js +4 -3
  46. package/dist/overrides/proxyExternalRequest/dummy.js +2 -1
  47. package/dist/overrides/proxyExternalRequest/node.js +2 -2
  48. package/dist/overrides/queue/dummy.js +2 -1
  49. package/dist/overrides/warmer/dummy.js +2 -1
  50. package/dist/overrides/wrappers/aws-lambda-streaming.d.ts +1 -1
  51. package/dist/overrides/wrappers/aws-lambda-streaming.js +0 -4
  52. package/dist/overrides/wrappers/aws-lambda.js +0 -3
  53. package/dist/overrides/wrappers/cloudflare-edge.d.ts +9 -0
  54. package/dist/overrides/wrappers/{cloudflare.js → cloudflare-edge.js} +3 -3
  55. package/dist/overrides/wrappers/cloudflare-node.d.ts +8 -0
  56. package/dist/overrides/wrappers/cloudflare-node.js +50 -0
  57. package/dist/overrides/wrappers/dummy.d.ts +2 -2
  58. package/dist/overrides/wrappers/dummy.js +1 -1
  59. package/dist/overrides/wrappers/node.d.ts +1 -1
  60. package/dist/overrides/wrappers/node.js +0 -3
  61. package/dist/plugins/edge.js +6 -1
  62. package/dist/plugins/resolve.js +5 -1
  63. package/dist/types/open-next.d.ts +26 -1
  64. package/dist/types/overrides.d.ts +4 -5
  65. package/dist/{adapters → utils}/binary.js +1 -0
  66. package/dist/utils/error.d.ts +1 -0
  67. package/dist/utils/error.js +8 -0
  68. package/package.json +3 -3
  69. package/dist/overrides/wrappers/cloudflare.d.ts +0 -12
  70. /package/dist/{adapters → utils}/binary.d.ts +0 -0
package/README.md CHANGED
@@ -77,18 +77,23 @@ To run `OpenNext` locally:
77
77
 
78
78
  1. Clone this repository.
79
79
  1. Build `open-next`:
80
+
80
81
  ```bash
81
82
  cd packages/open-next
82
83
  pnpm build
83
84
  ```
85
+
84
86
  1. Run `open-next` in watch mode:
87
+
85
88
  ```bash
86
89
  pnpm dev
87
90
  ```
91
+
88
92
  1. Now, you can make changes in `open-next` and build your Next.js app to test the changes.
93
+
89
94
  ```bash
90
95
  cd path/to/my/nextjs/app
91
- path/to/open-next/packages/open-next/dist/index.js build
96
+ path/to/opennextjs-aws/packages/open-next/dist/index.js build
92
97
  ```
93
98
 
94
99
  ### Coldstart
@@ -58,8 +58,7 @@ interface CacheHandlerValue {
58
58
  cacheState?: string;
59
59
  value: IncrementalCacheValue | null;
60
60
  }
61
- export declare function hasCacheExtension(key: string): boolean;
62
- export default class S3Cache {
61
+ export default class Cache {
63
62
  get(key: string, options?: boolean | {
64
63
  fetchCache?: boolean;
65
64
  kindHint?: "app" | "pages" | "fetch";
@@ -1,10 +1,5 @@
1
- import { isBinaryContentType } from "./binary";
1
+ import { isBinaryContentType } from "../utils/binary";
2
2
  import { debug, error, warn } from "./logger";
3
- /** Beginning single backslash is intentional, to look for the dot + the extension. Do not escape it again. */
4
- const CACHE_EXTENSION_REGEX = /\.(cache|fetch)$/;
5
- export function hasCacheExtension(key) {
6
- return CACHE_EXTENSION_REGEX.test(key);
7
- }
8
3
  function isFetchCache(options) {
9
4
  if (typeof options === "boolean") {
10
5
  return options;
@@ -17,7 +12,7 @@ function isFetchCache(options) {
17
12
  return false;
18
13
  }
19
14
  // We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time
20
- export default class S3Cache {
15
+ export default class Cache {
21
16
  async get(key,
22
17
  // fetchCache is for next 13.5 and above, kindHint is for next 14 and above and boolean is for earlier versions
23
18
  options) {
@@ -34,7 +29,6 @@ export default class S3Cache {
34
29
  debug("get fetch cache", { key, softTags, tags });
35
30
  try {
36
31
  const { value, lastModified } = await globalThis.incrementalCache.get(key, true);
37
- // const { Body, LastModified } = await this.getS3Object(key, "fetch");
38
32
  const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
39
33
  if (_lastModified === -1) {
40
34
  // If some tags are stale we need to force revalidation
@@ -71,10 +65,6 @@ export default class S3Cache {
71
65
  async getIncrementalCache(key) {
72
66
  try {
73
67
  const { value: cacheData, lastModified } = await globalThis.incrementalCache.get(key, false);
74
- // const { Body, LastModified } = await this.getS3Object(key, "cache");
75
- // const cacheData = JSON.parse(
76
- // (await Body?.transformToString()) ?? "{}",
77
- // ) as S3CachedFile;
78
68
  const meta = cacheData?.meta;
79
69
  const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
80
70
  if (_lastModified === -1) {
@@ -25,3 +25,5 @@ export declare const ConfigHeaders: import("../../types/next-types").Header[] |
25
25
  export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest;
26
26
  export declare const AppPathsManifestKeys: string[];
27
27
  export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
28
+ export declare const AppPathsManifest: Record<string, string>;
29
+ export declare const AppPathRoutesManifest: Record<string, string>;
@@ -1,8 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { debug } from "../logger";
3
- import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest,
4
- // loadPublicAssets,
5
- loadRoutesManifest, } from "./util.js";
3
+ import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
6
4
  export const NEXT_DIR = path.join(__dirname, ".next");
7
5
  export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
8
6
  debug({ NEXT_DIR, OPEN_NEXT_DIR });
@@ -19,3 +17,6 @@ export const AppPathsManifestKeys =
19
17
  /* @__PURE__ */ loadAppPathsManifestKeys(NEXT_DIR);
20
18
  export const MiddlewareManifest =
21
19
  /* @__PURE__ */ loadMiddlewareManifest(NEXT_DIR);
20
+ export const AppPathsManifest = /* @__PURE__ */ loadAppPathsManifest(NEXT_DIR);
21
+ export const AppPathRoutesManifest =
22
+ /* @__PURE__ */ loadAppPathRoutesManifest(NEXT_DIR);
@@ -24,5 +24,7 @@ export declare function loadRoutesManifest(nextDir: string): {
24
24
  };
25
25
  export declare function loadConfigHeaders(nextDir: string): import("../../types/next-types").Header[] | undefined;
26
26
  export declare function loadPrerenderManifest(nextDir: string): PrerenderManifest;
27
+ export declare function loadAppPathsManifest(nextDir: string): Record<string, string>;
28
+ export declare function loadAppPathRoutesManifest(nextDir: string): Record<string, string>;
27
29
  export declare function loadAppPathsManifestKeys(nextDir: string): string[];
28
30
  export declare function loadMiddlewareManifest(nextDir: string): MiddlewareManifest;
@@ -60,12 +60,22 @@ export function loadPrerenderManifest(nextDir) {
60
60
  const json = fs.readFileSync(filePath, "utf-8");
61
61
  return JSON.parse(json);
62
62
  }
63
- export function loadAppPathsManifestKeys(nextDir) {
63
+ export function loadAppPathsManifest(nextDir) {
64
64
  const appPathsManifestPath = path.join(nextDir, "server", "app-paths-manifest.json");
65
65
  const appPathsManifestJson = fs.existsSync(appPathsManifestPath)
66
66
  ? fs.readFileSync(appPathsManifestPath, "utf-8")
67
67
  : "{}";
68
- const appPathsManifest = JSON.parse(appPathsManifestJson);
68
+ return JSON.parse(appPathsManifestJson);
69
+ }
70
+ export function loadAppPathRoutesManifest(nextDir) {
71
+ const appPathRoutesManifestPath = path.join(nextDir, "app-path-routes-manifest.json");
72
+ if (fs.existsSync(appPathRoutesManifestPath)) {
73
+ return JSON.parse(fs.readFileSync(appPathRoutesManifestPath, "utf-8"));
74
+ }
75
+ return {};
76
+ }
77
+ export function loadAppPathsManifestKeys(nextDir) {
78
+ const appPathsManifest = loadAppPathsManifest(nextDir);
69
79
  return Object.keys(appPathsManifest).map((key) => {
70
80
  // Remove parallel route
71
81
  let cleanedKey = key.replace(/\/@[^\/]+/g, "");
@@ -1,4 +1,3 @@
1
- import type { StreamCreator } from "../http/openNextResponse.js";
2
- import type { InternalEvent, InternalResult } from "../types/open-next.js";
1
+ import type { InternalEvent, InternalResult, StreamCreator } from "../types/open-next.js";
3
2
  export declare const handler: (...args: any[]) => any;
4
3
  export declare function defaultHandler(event: InternalEvent, streamCreator?: StreamCreator): Promise<InternalResult>;
@@ -1,3 +1,4 @@
1
+ import { isOpenNextError } from "../utils/error";
1
2
  export function debug(...args) {
2
3
  if (globalThis.openNextDebug) {
3
4
  console.log(...args);
@@ -22,23 +23,24 @@ export function error(...args) {
22
23
  if (args.some((arg) => isDownplayedErrorLog(arg))) {
23
24
  debug(...args);
24
25
  }
25
- else if (args.some((arg) => arg.__openNextInternal)) {
26
+ else if (args.some((arg) => isOpenNextError(arg))) {
26
27
  // In case of an internal error, we log it with the appropriate log level
27
- const error = args.find((arg) => arg.__openNextInternal);
28
- if (error.logLevel === 0) {
29
- debug(...args);
28
+ const error = args.find((arg) => isOpenNextError(arg));
29
+ if (error.logLevel < getOpenNextErrorLogLevel()) {
30
30
  return;
31
31
  }
32
+ if (error.logLevel === 0) {
33
+ // Display the name and the message instead of full Open Next errors.
34
+ // console.log is used so that logging does not depend on openNextDebug.
35
+ return console.log(...args.map((arg) => isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg));
36
+ }
32
37
  if (error.logLevel === 1) {
33
- warn(...args);
34
- return;
38
+ // Display the name and the message instead of full Open Next errors.
39
+ return warn(...args.map((arg) => isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg));
35
40
  }
36
- console.error(...args);
37
- return;
38
- }
39
- else {
40
- console.error(...args);
41
+ return console.error(...args);
41
42
  }
43
+ console.error(...args);
42
44
  }
43
45
  export const awsLogger = {
44
46
  trace: () => { },
@@ -47,3 +49,22 @@ export const awsLogger = {
47
49
  warn,
48
50
  error,
49
51
  };
52
+ /**
53
+ * Retrieves the log level for internal errors from the
54
+ * OPEN_NEXT_ERROR_LOG_LEVEL environment variable.
55
+ *
56
+ * @returns The numerical log level 0 (debug), 1 (warn), or 2 (error)
57
+ */
58
+ function getOpenNextErrorLogLevel() {
59
+ const strLevel = process.env.OPEN_NEXT_ERROR_LOG_LEVEL ?? "1";
60
+ switch (strLevel.toLowerCase()) {
61
+ case "debug":
62
+ case "0":
63
+ return 0;
64
+ case "error":
65
+ case "2":
66
+ return 2;
67
+ default:
68
+ return 1;
69
+ }
70
+ }
@@ -2,7 +2,7 @@ import { runWithOpenNextRequestContext } from "../utils/promise";
2
2
  import { debug, error } from "../adapters/logger";
3
3
  import { createGenericHandler } from "../core/createGenericHandler";
4
4
  import { resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
5
- import routingHandler from "../core/routingHandler";
5
+ import routingHandler, { INTERNAL_HEADER_INITIAL_PATH, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
6
6
  globalThis.internalFetch = fetch;
7
7
  globalThis.__openNextAls = new AsyncLocalStorage();
8
8
  const defaultHandler = async (internalEvent) => {
@@ -22,10 +22,19 @@ const defaultHandler = async (internalEvent) => {
22
22
  const origin = await originResolver.resolve(result.internalEvent.rawPath);
23
23
  return {
24
24
  type: "middleware",
25
- internalEvent: result.internalEvent,
25
+ internalEvent: {
26
+ ...result.internalEvent,
27
+ headers: {
28
+ ...result.internalEvent.headers,
29
+ [INTERNAL_HEADER_INITIAL_PATH]: internalEvent.rawPath,
30
+ [INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes) ?? "[]",
31
+ },
32
+ },
26
33
  isExternalRewrite: result.isExternalRewrite,
27
34
  origin,
28
35
  isISR: result.isISR,
36
+ initialPath: result.initialPath,
37
+ resolvedRoutes: result.resolvedRoutes,
29
38
  };
30
39
  }
31
40
  try {
@@ -45,6 +54,8 @@ const defaultHandler = async (internalEvent) => {
45
54
  isExternalRewrite: false,
46
55
  origin: false,
47
56
  isISR: result.isISR,
57
+ initialPath: result.internalEvent.rawPath,
58
+ resolvedRoutes: [{ route: "/500", type: "page" }],
48
59
  };
49
60
  }
50
61
  }
@@ -8,4 +8,5 @@ export declare function optimizeImage(headers: APIGatewayProxyEventHeaders, imag
8
8
  maxAge: number;
9
9
  etag: string;
10
10
  upstreamEtag: string;
11
+ error?: unknown;
11
12
  }>;
@@ -5,7 +5,6 @@ import { debug } from "../../logger.js";
5
5
  export async function optimizeImage(headers, imageParams, nextConfig, handleRequest) {
6
6
  const result = await imageOptimizer({ headers }, {}, // res object is not necessary as it's not actually used.
7
7
  imageParams, nextConfig, false, // not in dev mode
8
- // @ts-expect-error - This file is used only for Next 14.1 and below. Typing is correct for these versions.
9
8
  handleRequest);
10
9
  debug("optimized result", result);
11
10
  return result;
@@ -8,4 +8,5 @@ export declare function optimizeImage(headers: APIGatewayProxyEventHeaders, imag
8
8
  maxAge: number;
9
9
  etag: string;
10
10
  upstreamEtag: string;
11
+ error?: unknown;
11
12
  }>;
@@ -1,6 +1,10 @@
1
1
  //TODO: We should probably move all the utils to a separate location
2
2
  export function setNodeEnv() {
3
- process.env.NODE_ENV = process.env.NODE_ENV ?? "production";
3
+ // Note: we create a `processEnv` variable instead of just using `process.env` directly
4
+ // because build tools can substitute `process.env.NODE_ENV` on build making
5
+ // assignments such as `process.env.NODE_ENV = ...` problematic
6
+ const processEnv = process.env;
7
+ processEnv.NODE_ENV = process.env.NODE_ENV ?? "production";
4
8
  }
5
9
  export function generateUniqueId() {
6
10
  return Math.random().toString(36).slice(2, 8);
@@ -1,3 +1,3 @@
1
- export declare function bundleNextServer(outputDir: string, appPath: string, { debug }: {
2
- debug?: boolean | undefined;
1
+ export declare function bundleNextServer(outputDir: string, appPath: string, { minify }: {
2
+ minify?: boolean | undefined;
3
3
  }): Promise<void>;
@@ -35,7 +35,7 @@ const externals = [
35
35
  // Adding it cause to add a lot of unnecessary deps
36
36
  "next/dist/compiled/next-server",
37
37
  ];
38
- export async function bundleNextServer(outputDir, appPath, { debug = false }) {
38
+ export async function bundleNextServer(outputDir, appPath, { minify = true }) {
39
39
  const require = createRequire(`${appPath}/package.json`);
40
40
  const entrypoint = require.resolve("next/dist/esm/server/next-server.js");
41
41
  await build({
@@ -46,7 +46,7 @@ export async function bundleNextServer(outputDir, appPath, { debug = false }) {
46
46
  // packages: "external",
47
47
  format: "cjs",
48
48
  external: externals,
49
- minify: !debug,
49
+ minify,
50
50
  outfile: path.join(outputDir, "next-server.runtime.prod.js"),
51
51
  sourcemap: false,
52
52
  plugins: [
@@ -79,7 +79,7 @@ File ${fullFilePath} does not exist
79
79
  }
80
80
  catch (e) {
81
81
  if (alternativePath) {
82
- computeCopyFilesForPage(alternativePath);
82
+ safeComputeCopyFilesForPage(alternativePath);
83
83
  }
84
84
  }
85
85
  };
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { isBinaryContentType } from "../adapters/binary.js";
4
3
  import logger from "../logger.js";
4
+ import { isBinaryContentType } from "../utils/binary.js";
5
5
  import * as buildHelper from "./helper.js";
6
6
  export function createStaticAssets(options) {
7
7
  logger.info("Bundling static assets...");
@@ -109,10 +109,29 @@ export function createCacheAssets(options) {
109
109
  };
110
110
  fs.writeFileSync(cacheFilePath, JSON.stringify(cacheFileContent));
111
111
  });
112
+ // We need to traverse the cache to find every .meta file
113
+ const metaFiles = [];
114
+ // Copy fetch-cache to cache folder
115
+ const fetchCachePath = path.join(appBuildOutputPath, ".next/cache/fetch-cache");
116
+ if (fs.existsSync(fetchCachePath)) {
117
+ const fetchOutputPath = path.join(outputDir, "cache", "__fetch", buildId);
118
+ fs.mkdirSync(fetchOutputPath, { recursive: true });
119
+ fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
120
+ buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
121
+ const fileContent = fs.readFileSync(absolutePath, "utf8");
122
+ const fileData = JSON.parse(fileContent);
123
+ fileData?.tags?.forEach((tag) => {
124
+ metaFiles.push({
125
+ tag: { S: path.posix.join(buildId, tag) },
126
+ path: {
127
+ S: path.posix.join(buildId, relativePath),
128
+ },
129
+ revalidatedAt: { N: "1" },
130
+ });
131
+ });
132
+ });
133
+ }
112
134
  if (!options.config.dangerous?.disableTagCache) {
113
- // Generate dynamodb data
114
- // We need to traverse the cache to find every .meta file
115
- const metaFiles = [];
116
135
  // Compute dynamodb cache data
117
136
  // Traverse files inside cache to find all meta files and cache tags associated with them
118
137
  buildHelper.traverseFiles(outputPath, ({ absolutePath }) => absolutePath.endsWith(".meta"), ({ absolutePath, relativePath }) => {
@@ -134,26 +153,6 @@ export function createCacheAssets(options) {
134
153
  });
135
154
  }
136
155
  });
137
- // Copy fetch-cache to cache folder
138
- const fetchCachePath = path.join(appBuildOutputPath, ".next/cache/fetch-cache");
139
- if (fs.existsSync(fetchCachePath)) {
140
- const fetchOutputPath = path.join(outputDir, "cache", "__fetch", buildId);
141
- fs.mkdirSync(fetchOutputPath, { recursive: true });
142
- fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
143
- buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
144
- const fileContent = fs.readFileSync(absolutePath, "utf8");
145
- const fileData = JSON.parse(fileContent);
146
- fileData?.tags?.forEach((tag) => {
147
- metaFiles.push({
148
- tag: { S: path.posix.join(buildId, tag) },
149
- path: {
150
- S: path.posix.join(buildId, relativePath),
151
- },
152
- revalidatedAt: { N: "1" },
153
- });
154
- });
155
- });
156
- }
157
156
  if (metaFiles.length > 0) {
158
157
  useTagCache = true;
159
158
  const providerPath = path.join(outputDir, "dynamodb-provider");
@@ -20,7 +20,7 @@ export async function createMiddleware(options, { forceOnlyBuildOnce = false } =
20
20
  const outputPath = path.join(outputDir, "middleware");
21
21
  fs.mkdirSync(outputPath, { recursive: true });
22
22
  // Copy open-next.config.mjs
23
- buildHelper.copyOpenNextConfig(options.buildDir, outputPath, config.middleware.override?.wrapper === "cloudflare");
23
+ buildHelper.copyOpenNextConfig(options.buildDir, outputPath, await buildHelper.isEdgeRuntime(config.middleware.override));
24
24
  // Bundle middleware
25
25
  await buildEdgeBundle({
26
26
  entrypoint: path.join(options.openNextDistDir, "adapters", "middleware.js"),
@@ -77,7 +77,6 @@ async function generateBundle(name, options, fnOptions) {
77
77
  // `node_modules` inside `.next/standalone`, and others inside
78
78
  // `.next/standalone/package/path` (ie. `.next`, `server.js`).
79
79
  // We need to output the handler file inside the package path.
80
- const isMonorepo = monorepoRoot !== appPath;
81
80
  const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
82
81
  fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true });
83
82
  const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs";
@@ -89,7 +88,7 @@ async function generateBundle(name, options, fnOptions) {
89
88
  const isBundled = fnOptions.experimentalBundledNextServer ?? false;
90
89
  if (isBundled) {
91
90
  await bundleNextServer(path.join(outputPath, packagePath), appPath, {
92
- debug: options.debug,
91
+ minify: options.minify,
93
92
  });
94
93
  }
95
94
  // Copy middleware
@@ -166,6 +165,7 @@ async function generateBundle(name, options, fnOptions) {
166
165
  : {}),
167
166
  },
168
167
  }, options);
168
+ const isMonorepo = monorepoRoot !== appPath;
169
169
  if (isMonorepo) {
170
170
  addMonorepoEntrypoint(outputPath, packagePath);
171
171
  }
@@ -208,7 +208,7 @@ function addMonorepoEntrypoint(outputPath, packagePath) {
208
208
  // that re-exports the real handler.
209
209
  // Always use posix path for import path
210
210
  const packagePosixPath = packagePath.split(path.sep).join(path.posix.sep);
211
- fs.writeFileSync(path.join(outputPath, "index.mjs"), [`export * from "./${packagePosixPath}/index.mjs";`].join(""));
211
+ fs.writeFileSync(path.join(outputPath, "index.mjs"), `export * from "./${packagePosixPath}/index.mjs";`);
212
212
  }
213
213
  async function minifyServerBundle(outputDir) {
214
214
  logger.info("Minimizing server function...");
@@ -1,15 +1,16 @@
1
1
  import type { MiddlewareInfo } from "../../types/next-types";
2
2
  import type { IncludedConverter, IncludedOriginResolver, LazyLoadedOverride, OverrideOptions, SplittedFunctionOptions } from "../../types/open-next";
3
3
  import type { OriginResolver } from "../../types/overrides.js";
4
- import type { BuildOptions } from "../helper.js";
4
+ import { type BuildOptions } from "../helper.js";
5
+ type Override = OverrideOptions & {
6
+ originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
7
+ };
5
8
  interface BuildEdgeBundleOptions {
6
9
  middlewareInfo?: MiddlewareInfo;
7
10
  entrypoint: string;
8
11
  outfile: string;
9
12
  options: BuildOptions;
10
- overrides?: OverrideOptions & {
11
- originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
12
- };
13
+ overrides?: Override;
13
14
  defaultConverter?: IncludedConverter;
14
15
  additionalInject?: string;
15
16
  includeCache?: boolean;
@@ -6,11 +6,15 @@ import logger from "../../logger.js";
6
6
  import { openNextEdgePlugins } from "../../plugins/edge.js";
7
7
  import { openNextReplacementPlugin } from "../../plugins/replacement.js";
8
8
  import { openNextResolvePlugin } from "../../plugins/resolve.js";
9
+ import { isEdgeRuntime } from "../helper.js";
9
10
  import { copyOpenNextConfig, esbuildAsync } from "../helper.js";
10
11
  export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, }) {
11
- const isInCloudfare = typeof overrides?.wrapper === "string"
12
- ? overrides.wrapper === "cloudflare"
13
- : (await overrides?.wrapper?.())?.edgeRuntime;
12
+ const isInCloudfare = await isEdgeRuntime(overrides);
13
+ function override(target) {
14
+ return typeof overrides?.[target] === "string"
15
+ ? overrides[target]
16
+ : undefined;
17
+ }
14
18
  await esbuildAsync({
15
19
  entryPoints: [entrypoint],
16
20
  bundle: true,
@@ -21,31 +25,17 @@ export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, opt
21
25
  plugins: [
22
26
  openNextResolvePlugin({
23
27
  overrides: {
24
- wrapper: typeof overrides?.wrapper === "string"
25
- ? overrides.wrapper
26
- : "aws-lambda",
27
- converter: typeof overrides?.converter === "string"
28
- ? overrides.converter
29
- : defaultConverter,
28
+ wrapper: override("wrapper") ?? "aws-lambda",
29
+ converter: override("converter") ?? defaultConverter,
30
30
  ...(includeCache
31
31
  ? {
32
- tagCache: typeof overrides?.tagCache === "string"
33
- ? overrides.tagCache
34
- : "dynamodb-lite",
35
- incrementalCache: typeof overrides?.incrementalCache === "string"
36
- ? overrides.incrementalCache
37
- : "s3-lite",
38
- queue: typeof overrides?.queue === "string"
39
- ? overrides.queue
40
- : "sqs-lite",
32
+ tagCache: override("tagCache") ?? "dynamodb-lite",
33
+ incrementalCache: override("incrementalCache") ?? "s3-lite",
34
+ queue: override("queue") ?? "sqs-lite",
41
35
  }
42
36
  : {}),
43
- originResolver: typeof overrides?.originResolver === "string"
44
- ? overrides.originResolver
45
- : "pattern-env",
46
- proxyExternalRequest: typeof overrides?.proxyExternalRequest === "string"
47
- ? overrides.proxyExternalRequest
48
- : "node",
37
+ originResolver: override("originResolver") ?? "pattern-env",
38
+ proxyExternalRequest: override("proxyExternalRequest") ?? "node",
49
39
  },
50
40
  fnName: name,
51
41
  }),
@@ -76,20 +66,32 @@ globalThis.Buffer = Buffer;
76
66
 
77
67
  import {AsyncLocalStorage} from "node:async_hooks";
78
68
  globalThis.AsyncLocalStorage = AsyncLocalStorage;
69
+
70
+ ${""
71
+ /**
72
+ * Next.js sets this `__import_unsupported` on `globalThis` (with `configurable: false`):
73
+ * https://github.com/vercel/next.js/blob/5b7833e3/packages/next/src/server/web/globals.ts#L94-L98
74
+ *
75
+ * It does so in both the middleware and the main server, so if the middleware runs in the same place
76
+ * as the main handler this code gets run twice triggering a runtime error.
77
+ *
78
+ * For this reason we need to patch `Object.defineProperty` to avoid this issue.
79
+ */
80
+ }
81
+ const defaultDefineProperty = Object.defineProperty;
82
+ Object.defineProperty = function(o, p, a) {
83
+ if(p=== '__import_unsupported' && Boolean(globalThis.__import_unsupported)) {
84
+ return;
85
+ }
86
+ return defaultDefineProperty(o, p, a);
87
+ };
88
+
79
89
  ${isInCloudfare
80
90
  ? ""
81
91
  : `
82
92
  const require = (await import("node:module")).createRequire(import.meta.url);
83
93
  const __filename = (await import("node:url")).fileURLToPath(import.meta.url);
84
94
  const __dirname = (await import("node:path")).dirname(__filename);
85
-
86
- const defaultDefineProperty = Object.defineProperty;
87
- Object.defineProperty = function(o, p, a) {
88
- if(p=== '__import_unsupported' && Boolean(globalThis.__import_unsupported)) {
89
- return;
90
- }
91
- return defaultDefineProperty(o, p, a);
92
- };
93
95
  `}
94
96
  ${additionalInject ?? ""}
95
97
  `,
@@ -101,7 +103,7 @@ globalThis.AsyncLocalStorage = AsyncLocalStorage;
101
103
  outfile,
102
104
  allowOverwrite: true,
103
105
  bundle: true,
104
- minify: !options.debug,
106
+ minify: options.minify,
105
107
  platform: "node",
106
108
  format: "esm",
107
109
  conditions: ["workerd", "worker", "browser"],
@@ -1,5 +1,5 @@
1
1
  import type { BuildOptions as ESBuildOptions } from "esbuild";
2
- import type { OpenNextConfig } from "../types/open-next.js";
2
+ import type { DefaultOverrideOptions, OpenNextConfig } from "../types/open-next.js";
3
3
  export type BuildOptions = ReturnType<typeof normalizeOptions>;
4
4
  export declare function normalizeOptions(config: OpenNextConfig, distDir: string, tempBuildDir: string): {
5
5
  appBuildOutputPath: string;
@@ -9,6 +9,7 @@ export declare function normalizeOptions(config: OpenNextConfig, distDir: string
9
9
  buildDir: string;
10
10
  config: OpenNextConfig;
11
11
  debug: boolean;
12
+ minify: boolean;
12
13
  monorepoRoot: string;
13
14
  nextVersion: string;
14
15
  openNextVersion: string;
@@ -68,3 +69,7 @@ export declare function printOpenNextVersion(options: BuildOptions): void;
68
69
  * the app directory, but the compiled config is in the root of the monorepo.
69
70
  */
70
71
  export declare function initOutputDir(options: BuildOptions): void;
72
+ /**
73
+ * @returns Whether the edge runtime is used
74
+ */
75
+ export declare function isEdgeRuntime(overrides: DefaultOverrideOptions | undefined): Promise<boolean | undefined>;
@@ -21,6 +21,7 @@ export function normalizeOptions(config, distDir, tempBuildDir) {
21
21
  else {
22
22
  appPackageJsonPath = findNextPackageJsonPath(appPath, monorepoRoot);
23
23
  }
24
+ const debug = Boolean(process.env.OPEN_NEXT_DEBUG) ?? false;
24
25
  return {
25
26
  appBuildOutputPath: buildOutputPath,
26
27
  appPackageJsonPath,
@@ -28,7 +29,9 @@ export function normalizeOptions(config, distDir, tempBuildDir) {
28
29
  appPublicPath: path.join(appPath, "public"),
29
30
  buildDir: path.join(outputDir, ".build"),
30
31
  config,
31
- debug: Boolean(process.env.OPEN_NEXT_DEBUG) ?? false,
32
+ debug,
33
+ // Whether ESBuild should minify the code
34
+ minify: !debug,
32
35
  monorepoRoot,
33
36
  nextVersion: getNextVersion(appPath),
34
37
  openNextVersion: getOpenNextVersion(),
@@ -68,13 +71,13 @@ function findNextPackageJsonPath(appPath, root) {
68
71
  : path.join(root, "./package.json");
69
72
  }
70
73
  export function esbuildSync(esbuildOptions, options) {
71
- const { openNextVersion, debug } = options;
74
+ const { openNextVersion, debug, minify } = options;
72
75
  const result = buildSync({
73
76
  target: "esnext",
74
77
  format: "esm",
75
78
  platform: "node",
76
79
  bundle: true,
77
- minify: !debug,
80
+ minify,
78
81
  mainFields: ["module", "main"],
79
82
  sourcemap: debug ? "inline" : false,
80
83
  sourcesContent: false,
@@ -95,13 +98,13 @@ export function esbuildSync(esbuildOptions, options) {
95
98
  }
96
99
  }
97
100
  export async function esbuildAsync(esbuildOptions, options) {
98
- const { openNextVersion, debug } = options;
101
+ const { openNextVersion, debug, minify } = options;
99
102
  const result = await buildAsync({
100
103
  target: "esnext",
101
104
  format: "esm",
102
105
  platform: "node",
103
106
  bundle: true,
104
- minify: !debug,
107
+ minify,
105
108
  mainFields: ["module", "main"],
106
109
  sourcemap: debug ? "inline" : false,
107
110
  sourcesContent: false,
@@ -261,3 +264,15 @@ export function initOutputDir(options) {
261
264
  fs.mkdirSync(buildDir, { recursive: true });
262
265
  fs.cpSync(options.tempBuildDir, buildDir, { recursive: true });
263
266
  }
267
+ /**
268
+ * @returns Whether the edge runtime is used
269
+ */
270
+ export async function isEdgeRuntime(overrides) {
271
+ if (!overrides?.wrapper) {
272
+ return false;
273
+ }
274
+ if (typeof overrides.wrapper === "string") {
275
+ return ["cloudflare-edge", "cloudflare"].includes(overrides.wrapper);
276
+ }
277
+ return (await overrides?.wrapper?.())?.edgeRuntime;
278
+ }