@opennextjs/aws 3.1.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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/adapters/binary.d.ts +1 -0
  4. package/dist/adapters/binary.js +63 -0
  5. package/dist/adapters/cache.d.ts +96 -0
  6. package/dist/adapters/cache.js +278 -0
  7. package/dist/adapters/config/index.d.ts +27 -0
  8. package/dist/adapters/config/index.js +21 -0
  9. package/dist/adapters/config/util.d.ts +28 -0
  10. package/dist/adapters/config/util.js +84 -0
  11. package/dist/adapters/dynamo-provider.d.ts +8 -0
  12. package/dist/adapters/dynamo-provider.js +42 -0
  13. package/dist/adapters/edge-adapter.d.ts +8 -0
  14. package/dist/adapters/edge-adapter.js +54 -0
  15. package/dist/adapters/image-optimization-adapter.d.ts +4 -0
  16. package/dist/adapters/image-optimization-adapter.js +193 -0
  17. package/dist/adapters/logger.d.ts +13 -0
  18. package/dist/adapters/logger.js +51 -0
  19. package/dist/adapters/middleware.d.ts +5 -0
  20. package/dist/adapters/middleware.js +84 -0
  21. package/dist/adapters/plugins/14.1/util.d.ts +1 -0
  22. package/dist/adapters/plugins/14.1/util.js +29 -0
  23. package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +11 -0
  24. package/dist/adapters/plugins/image-optimization/image-optimization.js +14 -0
  25. package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +11 -0
  26. package/dist/adapters/plugins/image-optimization/image-optimization.replacement.js +22 -0
  27. package/dist/adapters/plugins/without-routing/requestHandler.d.ts +2 -0
  28. package/dist/adapters/plugins/without-routing/requestHandler.js +10 -0
  29. package/dist/adapters/revalidate.d.ts +9 -0
  30. package/dist/adapters/revalidate.js +71 -0
  31. package/dist/adapters/server-adapter.d.ts +4 -0
  32. package/dist/adapters/server-adapter.js +27 -0
  33. package/dist/adapters/util.d.ts +10 -0
  34. package/dist/adapters/util.js +28 -0
  35. package/dist/adapters/warmer-function.d.ts +12 -0
  36. package/dist/adapters/warmer-function.js +80 -0
  37. package/dist/build/bundleNextServer.d.ts +1 -0
  38. package/dist/build/bundleNextServer.js +87 -0
  39. package/dist/build/compileConfig.d.ts +3 -0
  40. package/dist/build/compileConfig.js +64 -0
  41. package/dist/build/copyTracedFiles.d.ts +1 -0
  42. package/dist/build/copyTracedFiles.js +207 -0
  43. package/dist/build/createServerBundle.d.ts +3 -0
  44. package/dist/build/createServerBundle.js +228 -0
  45. package/dist/build/edge/createEdgeBundle.d.ts +20 -0
  46. package/dist/build/edge/createEdgeBundle.js +149 -0
  47. package/dist/build/generateOutput.d.ts +2 -0
  48. package/dist/build/generateOutput.js +239 -0
  49. package/dist/build/helper.d.ts +33 -0
  50. package/dist/build/helper.js +193 -0
  51. package/dist/build/patch/patchedAsyncStorage.d.ts +5 -0
  52. package/dist/build/patch/patchedAsyncStorage.js +16 -0
  53. package/dist/build/validateConfig.d.ts +2 -0
  54. package/dist/build/validateConfig.js +76 -0
  55. package/dist/build.d.ts +7 -0
  56. package/dist/build.js +533 -0
  57. package/dist/cache/incremental/s3-lite.d.ts +3 -0
  58. package/dist/cache/incremental/s3-lite.js +72 -0
  59. package/dist/cache/incremental/s3.d.ts +3 -0
  60. package/dist/cache/incremental/s3.js +44 -0
  61. package/dist/cache/incremental/types.d.ts +34 -0
  62. package/dist/cache/incremental/types.js +1 -0
  63. package/dist/cache/next-types.d.ts +62 -0
  64. package/dist/cache/next-types.js +1 -0
  65. package/dist/cache/tag/constants.d.ts +2 -0
  66. package/dist/cache/tag/constants.js +17 -0
  67. package/dist/cache/tag/dynamodb-lite.d.ts +3 -0
  68. package/dist/cache/tag/dynamodb-lite.js +174 -0
  69. package/dist/cache/tag/dynamodb.d.ts +3 -0
  70. package/dist/cache/tag/dynamodb.js +129 -0
  71. package/dist/cache/tag/types.d.ts +11 -0
  72. package/dist/cache/tag/types.js +1 -0
  73. package/dist/converters/aws-apigw-v1.d.ts +3 -0
  74. package/dist/converters/aws-apigw-v1.js +89 -0
  75. package/dist/converters/aws-apigw-v2.d.ts +3 -0
  76. package/dist/converters/aws-apigw-v2.js +99 -0
  77. package/dist/converters/aws-cloudfront.d.ts +3 -0
  78. package/dist/converters/aws-cloudfront.js +170 -0
  79. package/dist/converters/dummy.d.ts +7 -0
  80. package/dist/converters/dummy.js +16 -0
  81. package/dist/converters/edge.d.ts +6 -0
  82. package/dist/converters/edge.js +88 -0
  83. package/dist/converters/node.d.ts +3 -0
  84. package/dist/converters/node.js +45 -0
  85. package/dist/converters/sqs-revalidate.d.ts +4 -0
  86. package/dist/converters/sqs-revalidate.js +22 -0
  87. package/dist/converters/utils.d.ts +1 -0
  88. package/dist/converters/utils.js +9 -0
  89. package/dist/core/createGenericHandler.d.ts +11 -0
  90. package/dist/core/createGenericHandler.js +16 -0
  91. package/dist/core/createMainHandler.d.ts +18 -0
  92. package/dist/core/createMainHandler.js +24 -0
  93. package/dist/core/edgeFunctionHandler.d.ts +54 -0
  94. package/dist/core/edgeFunctionHandler.js +21 -0
  95. package/dist/core/patchAsyncStorage.d.ts +1 -0
  96. package/dist/core/patchAsyncStorage.js +16 -0
  97. package/dist/core/requestHandler.d.ts +3 -0
  98. package/dist/core/requestHandler.js +162 -0
  99. package/dist/core/require-hooks.d.ts +3 -0
  100. package/dist/core/require-hooks.js +102 -0
  101. package/dist/core/resolve.d.ts +31 -0
  102. package/dist/core/resolve.js +81 -0
  103. package/dist/core/routing/cacheInterceptor.d.ts +2 -0
  104. package/dist/core/routing/cacheInterceptor.js +162 -0
  105. package/dist/core/routing/i18n/accept-header.d.ts +1 -0
  106. package/dist/core/routing/i18n/accept-header.js +101 -0
  107. package/dist/core/routing/i18n/index.d.ts +2 -0
  108. package/dist/core/routing/i18n/index.js +36 -0
  109. package/dist/core/routing/matcher.d.ts +25 -0
  110. package/dist/core/routing/matcher.js +294 -0
  111. package/dist/core/routing/middleware.d.ts +7 -0
  112. package/dist/core/routing/middleware.js +167 -0
  113. package/dist/core/routing/util.d.ts +105 -0
  114. package/dist/core/routing/util.js +415 -0
  115. package/dist/core/routingHandler.d.ts +8 -0
  116. package/dist/core/routingHandler.js +127 -0
  117. package/dist/core/util.d.ts +5 -0
  118. package/dist/core/util.js +84 -0
  119. package/dist/helpers/withCloudflare.d.ts +65 -0
  120. package/dist/helpers/withCloudflare.js +58 -0
  121. package/dist/helpers/withSST.d.ts +54 -0
  122. package/dist/helpers/withSST.js +28 -0
  123. package/dist/http/index.d.ts +2 -0
  124. package/dist/http/index.js +4 -0
  125. package/dist/http/openNextResponse.d.ts +78 -0
  126. package/dist/http/openNextResponse.js +281 -0
  127. package/dist/http/request.d.ts +12 -0
  128. package/dist/http/request.js +35 -0
  129. package/dist/http/util.d.ts +4 -0
  130. package/dist/http/util.js +34 -0
  131. package/dist/index.d.ts +2 -0
  132. package/dist/index.js +37 -0
  133. package/dist/logger.d.ts +12 -0
  134. package/dist/logger.js +13 -0
  135. package/dist/minimize-js.d.ts +1 -0
  136. package/dist/minimize-js.js +101 -0
  137. package/dist/overrides/imageLoader/host.d.ts +3 -0
  138. package/dist/overrides/imageLoader/host.js +29 -0
  139. package/dist/overrides/imageLoader/s3.d.ts +3 -0
  140. package/dist/overrides/imageLoader/s3.js +33 -0
  141. package/dist/plugins/edge.d.ts +15 -0
  142. package/dist/plugins/edge.js +157 -0
  143. package/dist/plugins/replacement.d.ts +42 -0
  144. package/dist/plugins/replacement.js +74 -0
  145. package/dist/plugins/resolve.d.ts +18 -0
  146. package/dist/plugins/resolve.js +45 -0
  147. package/dist/queue/sqs-lite.d.ts +3 -0
  148. package/dist/queue/sqs-lite.js +52 -0
  149. package/dist/queue/sqs.d.ts +3 -0
  150. package/dist/queue/sqs.js +20 -0
  151. package/dist/queue/types.d.ts +12 -0
  152. package/dist/queue/types.js +1 -0
  153. package/dist/types/aws-lambda.d.ts +22 -0
  154. package/dist/types/aws-lambda.js +1 -0
  155. package/dist/types/next-types.d.ts +155 -0
  156. package/dist/types/next-types.js +2 -0
  157. package/dist/types/open-next.d.ts +303 -0
  158. package/dist/types/open-next.js +1 -0
  159. package/dist/utils/error.d.ts +23 -0
  160. package/dist/utils/error.js +32 -0
  161. package/dist/utils/fetch.d.ts +2 -0
  162. package/dist/utils/fetch.js +23 -0
  163. package/dist/utils/promise.d.ts +18 -0
  164. package/dist/utils/promise.js +48 -0
  165. package/dist/utils/stream.d.ts +5 -0
  166. package/dist/utils/stream.js +30 -0
  167. package/dist/wrappers/aws-lambda-streaming.d.ts +7 -0
  168. package/dist/wrappers/aws-lambda-streaming.js +88 -0
  169. package/dist/wrappers/aws-lambda.d.ts +7 -0
  170. package/dist/wrappers/aws-lambda.js +39 -0
  171. package/dist/wrappers/cloudflare.d.ts +11 -0
  172. package/dist/wrappers/cloudflare.js +20 -0
  173. package/dist/wrappers/node.d.ts +7 -0
  174. package/dist/wrappers/node.js +57 -0
  175. package/package.json +66 -0
@@ -0,0 +1,9 @@
1
+ export interface RevalidateEvent {
2
+ type: "revalidate";
3
+ records: {
4
+ host: string;
5
+ url: string;
6
+ id: string;
7
+ }[];
8
+ }
9
+ export declare const handler: (...args: any[]) => any;
@@ -0,0 +1,71 @@
1
+ import fs from "node:fs";
2
+ import https from "node:https";
3
+ import path from "node:path";
4
+ import { createGenericHandler } from "../core/createGenericHandler.js";
5
+ import { debug, error } from "./logger.js";
6
+ const prerenderManifest = loadPrerenderManifest();
7
+ const defaultHandler = async (event) => {
8
+ const failedRecords = [];
9
+ for (const record of event.records) {
10
+ const { host, url } = record;
11
+ debug(`Revalidating stale page`, { host, url });
12
+ // Make a HEAD request to the page to revalidate it. This will trigger
13
+ // the page to be re-rendered and cached in S3
14
+ // - HEAD request is used b/c it's not necessary to make a GET request
15
+ // and have CloudFront cache the request. This is because the request
16
+ // does not have real life headers and the cache won't be used anyway.
17
+ // - "previewModeId" is used to ensure the page is revalidated in a
18
+ // blocking way in lambda
19
+ // https://github.com/vercel/next.js/blob/1088b3f682cbe411be2d1edc502f8a090e36dee4/packages/next/src/server/api-utils/node.ts#L353
20
+ try {
21
+ await new Promise((resolve, reject) => {
22
+ const req = https.request(`https://${host}${url}`, {
23
+ method: "HEAD",
24
+ headers: {
25
+ "x-prerender-revalidate": prerenderManifest.preview.previewModeId,
26
+ "x-isr": "1",
27
+ },
28
+ }, (res) => {
29
+ debug("revalidating", {
30
+ url,
31
+ host,
32
+ headers: res.headers,
33
+ statusCode: res.statusCode,
34
+ });
35
+ if (res.statusCode !== 200 ||
36
+ res.headers["x-nextjs-cache"] !== "REVALIDATED") {
37
+ failedRecords.push(record);
38
+ }
39
+ resolve(res);
40
+ });
41
+ req.on("error", (err) => {
42
+ error(`Error revalidating page`, { host, url });
43
+ reject(err);
44
+ });
45
+ req.end();
46
+ });
47
+ }
48
+ catch (err) {
49
+ failedRecords.push(record);
50
+ }
51
+ }
52
+ if (failedRecords.length > 0) {
53
+ error(`Failed to revalidate ${failedRecords.length} pages`, {
54
+ failedRecords,
55
+ });
56
+ }
57
+ return {
58
+ type: "revalidate",
59
+ // Records returned here are the ones that failed to revalidate
60
+ records: failedRecords,
61
+ };
62
+ };
63
+ export const handler = await createGenericHandler({
64
+ handler: defaultHandler,
65
+ type: "revalidate",
66
+ });
67
+ function loadPrerenderManifest() {
68
+ const filePath = path.join("prerender-manifest.json");
69
+ const json = fs.readFileSync(filePath, "utf-8");
70
+ return JSON.parse(json);
71
+ }
@@ -0,0 +1,4 @@
1
+ declare global {
2
+ var internalFetch: typeof fetch;
3
+ }
4
+ export declare const handler: (...args: any[]) => any;
@@ -0,0 +1,27 @@
1
+ // We load every config here so that they are only loaded once
2
+ // and during cold starts
3
+ import { BuildId } from "./config/index.js";
4
+ import { createMainHandler } from "../core/createMainHandler.js";
5
+ import { setNodeEnv } from "./util.js";
6
+ // We load every config here so that they are only loaded once
7
+ // and during cold starts
8
+ setNodeEnv();
9
+ setBuildIdEnv();
10
+ setNextjsServerWorkingDirectory();
11
+ globalThis.internalFetch = fetch;
12
+ /////////////
13
+ // Handler //
14
+ /////////////
15
+ export const handler = await createMainHandler();
16
+ //////////////////////
17
+ // Helper functions //
18
+ //////////////////////
19
+ function setNextjsServerWorkingDirectory() {
20
+ // WORKAROUND: Set `NextServer` working directory (AWS specific) — https://github.com/serverless-stack/open-next#workaround-set-nextserver-working-directory-aws-specific
21
+ process.chdir(__dirname);
22
+ }
23
+ function setBuildIdEnv() {
24
+ // This allows users to access the CloudFront invalidating path when doing on-demand
25
+ // invalidations. ie. `/_next/data/${process.env.NEXT_BUILD_ID}/foo.json`
26
+ process.env.NEXT_BUILD_ID = BuildId;
27
+ }
@@ -0,0 +1,10 @@
1
+ export declare function setNodeEnv(): void;
2
+ export declare function generateUniqueId(): string;
3
+ /**
4
+ * Create an array of arrays of size `chunkSize` from `items`
5
+ * @param items Array of T
6
+ * @param chunkSize size of each chunk
7
+ * @returns T[][]
8
+ */
9
+ export declare function chunk<T>(items: T[], chunkSize: number): T[][];
10
+ export declare function parseNumberFromEnv(envValue: string | undefined): number | undefined;
@@ -0,0 +1,28 @@
1
+ //TODO: We should probably move all the utils to a separate location
2
+ export function setNodeEnv() {
3
+ process.env.NODE_ENV = process.env.NODE_ENV ?? "production";
4
+ }
5
+ export function generateUniqueId() {
6
+ return Math.random().toString(36).slice(2, 8);
7
+ }
8
+ /**
9
+ * Create an array of arrays of size `chunkSize` from `items`
10
+ * @param items Array of T
11
+ * @param chunkSize size of each chunk
12
+ * @returns T[][]
13
+ */
14
+ export function chunk(items, chunkSize) {
15
+ const chunked = items.reduce((acc, curr, i) => {
16
+ const chunkIndex = Math.floor(i / chunkSize);
17
+ acc[chunkIndex] = [...(acc[chunkIndex] ?? []), curr];
18
+ return acc;
19
+ }, new Array());
20
+ return chunked;
21
+ }
22
+ export function parseNumberFromEnv(envValue) {
23
+ if (typeof envValue !== "string") {
24
+ return envValue;
25
+ }
26
+ const parsedValue = parseInt(envValue);
27
+ return isNaN(parsedValue) ? undefined : parsedValue;
28
+ }
@@ -0,0 +1,12 @@
1
+ export interface WarmerEvent {
2
+ type: "warmer";
3
+ warmerId: string;
4
+ index: number;
5
+ concurrency: number;
6
+ delay: number;
7
+ }
8
+ export interface WarmerResponse {
9
+ type: "warmer";
10
+ serverId: string;
11
+ }
12
+ export declare const handler: (...args: any[]) => any;
@@ -0,0 +1,80 @@
1
+ import { createGenericHandler } from "../core/createGenericHandler.js";
2
+ import { debug, error } from "./logger.js";
3
+ import { generateUniqueId } from "./util.js";
4
+ const resolveWarmerInvoke = async () => {
5
+ const openNextParams = globalThis.openNextConfig.warmer;
6
+ if (typeof openNextParams?.invokeFunction === "function") {
7
+ return await openNextParams.invokeFunction();
8
+ }
9
+ else {
10
+ return Promise.resolve({
11
+ name: "aws-invoke",
12
+ invoke: async (warmerId) => {
13
+ const { InvokeCommand, LambdaClient } = await import("@aws-sdk/client-lambda");
14
+ const lambda = new LambdaClient({});
15
+ const warmParams = JSON.parse(process.env.WARM_PARAMS);
16
+ for (const warmParam of warmParams) {
17
+ const { concurrency: CONCURRENCY, function: FUNCTION_NAME } = warmParam;
18
+ debug({
19
+ event: "warmer invoked",
20
+ functionName: FUNCTION_NAME,
21
+ concurrency: CONCURRENCY,
22
+ warmerId,
23
+ });
24
+ const ret = await Promise.all(Array.from({ length: CONCURRENCY }, (_v, i) => i).map((i) => {
25
+ try {
26
+ return lambda.send(new InvokeCommand({
27
+ FunctionName: FUNCTION_NAME,
28
+ InvocationType: "RequestResponse",
29
+ Payload: Buffer.from(JSON.stringify({
30
+ type: "warmer",
31
+ warmerId,
32
+ index: i,
33
+ concurrency: CONCURRENCY,
34
+ delay: 75,
35
+ })),
36
+ }));
37
+ }
38
+ catch (e) {
39
+ error(`failed to warm up #${i}`, e);
40
+ // ignore error
41
+ }
42
+ }));
43
+ // Print status
44
+ const warmedServerIds = ret
45
+ .map((r, i) => {
46
+ if (r?.StatusCode !== 200 || !r?.Payload) {
47
+ error(`failed to warm up #${i}:`, r?.Payload?.toString());
48
+ return;
49
+ }
50
+ const payload = JSON.parse(Buffer.from(r.Payload).toString());
51
+ return {
52
+ statusCode: r.StatusCode,
53
+ payload,
54
+ type: "warmer",
55
+ };
56
+ })
57
+ .filter((r) => !!r);
58
+ debug({
59
+ event: "warmer result",
60
+ sent: CONCURRENCY,
61
+ success: warmedServerIds.length,
62
+ uniqueServersWarmed: [...new Set(warmedServerIds)].length,
63
+ });
64
+ }
65
+ },
66
+ });
67
+ }
68
+ };
69
+ export const handler = await createGenericHandler({
70
+ handler: defaultHandler,
71
+ type: "warmer",
72
+ });
73
+ async function defaultHandler() {
74
+ const warmerId = `warmer-${generateUniqueId()}`;
75
+ const invokeFn = await resolveWarmerInvoke();
76
+ await invokeFn.invoke(warmerId);
77
+ return {
78
+ type: "warmer",
79
+ };
80
+ }
@@ -0,0 +1 @@
1
+ export declare function bundleNextServer(outputDir: string, appPath: string): Promise<void>;
@@ -0,0 +1,87 @@
1
+ import { createRequire as topLevelCreateRequire } from "node:module";
2
+ import { build } from "esbuild";
3
+ import path from "path";
4
+ const externals = [
5
+ // This one was causing trouble, don't know why
6
+ "../experimental/testmode/server",
7
+ // sharedExternals
8
+ "styled-jsx",
9
+ "styled-jsx/style",
10
+ "@opentelemetry/api",
11
+ "next/dist/compiled/@next/react-dev-overlay/dist/middleware",
12
+ "next/dist/compiled/@ampproject/toolbox-optimizer",
13
+ "next/dist/compiled/edge-runtime",
14
+ "next/dist/compiled/@edge-runtime/ponyfill",
15
+ "next/dist/compiled/undici",
16
+ "next/dist/compiled/raw-body",
17
+ "next/dist/server/capsize-font-metrics.json",
18
+ "critters",
19
+ "next/dist/compiled/node-html-parser",
20
+ "next/dist/compiled/compression",
21
+ "next/dist/compiled/jsonwebtoken",
22
+ "next/dist/compiled/@opentelemetry/api",
23
+ "next/dist/compiled/@mswjs/interceptors/ClientRequest",
24
+ "next/dist/compiled/ws",
25
+ // externalsMap
26
+ // In the config they replace it, but we don't use this one inside NextServer anymore 13.4.12+
27
+ // For earlier versions we might have to alias it
28
+ "./web/sandbox",
29
+ // pagesExternal
30
+ "react",
31
+ "react-dom",
32
+ "react-server-dom-webpack",
33
+ "react-server-dom-turbopack",
34
+ // We need to remove this since this is what webpack is building
35
+ // Adding it cause to add a lot of unnecessary deps
36
+ "next/dist/compiled/next-server",
37
+ ];
38
+ export async function bundleNextServer(outputDir, appPath) {
39
+ const require = topLevelCreateRequire(`${appPath}/package.json`);
40
+ const entrypoint = require.resolve("next/dist/esm/server/next-server.js");
41
+ await build({
42
+ entryPoints: [entrypoint],
43
+ bundle: true,
44
+ platform: "node",
45
+ target: ["node18"],
46
+ // packages: "external",
47
+ format: "cjs",
48
+ external: externals,
49
+ minify: true,
50
+ outfile: path.join(outputDir, "next-server.runtime.prod.js"),
51
+ sourcemap: false,
52
+ plugins: [
53
+ {
54
+ name: "opennext-next-server",
55
+ setup(build) {
56
+ // This was an attempt at reducing server bundle size
57
+ // It might be the better way to go in the future
58
+ build.onResolve({ filter: /\.\/module.compiled/ }, (args) => {
59
+ const dir = args.resolveDir.split("/").slice(-1);
60
+ return {
61
+ path: path.join("next/dist/compiled/next-server/", `${dir}.runtime.prod.js`),
62
+ external: true,
63
+ };
64
+ });
65
+ build.onResolve({ filter: /[\\/]react-server\.node/ }, (args) => {
66
+ return {
67
+ path: args.path,
68
+ external: true,
69
+ };
70
+ });
71
+ build.onResolve({ filter: /vendored[\\/]rsc[\\/]entrypoints/ }, (args) => {
72
+ return {
73
+ path: args.path,
74
+ external: true,
75
+ };
76
+ });
77
+ build.onResolve({ filter: /\.external/ }, (args) => {
78
+ return {
79
+ path: args.path.replace(/\.\./, "next/dist"),
80
+ external: true,
81
+ };
82
+ });
83
+ },
84
+ },
85
+ ],
86
+ });
87
+ }
@@ -0,0 +1,3 @@
1
+ import { OpenNextConfig } from "../types/open-next.js";
2
+ export declare function compileOpenNextConfigNode(tempDir: string, openNextConfigPath?: string, nodeExternals?: string): string;
3
+ export declare function compileOpenNextConfigEdge(tempDir: string, config: OpenNextConfig, openNextConfigPath?: string): void;
@@ -0,0 +1,64 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { buildSync } from "esbuild";
4
+ import logger from "../logger.js";
5
+ export function compileOpenNextConfigNode(tempDir, openNextConfigPath, nodeExternals) {
6
+ const sourcePath = path.join(process.cwd(), openNextConfigPath ?? "open-next.config.ts");
7
+ const outputPath = path.join(tempDir, "open-next.config.mjs");
8
+ //Check if open-next.config.ts exists
9
+ if (!fs.existsSync(sourcePath)) {
10
+ //Create a simple open-next.config.mjs file
11
+ logger.debug("Cannot find open-next.config.ts. Using default config.");
12
+ fs.writeFileSync(outputPath, [
13
+ "var config = { default: { } };",
14
+ "var open_next_config_default = config;",
15
+ "export { open_next_config_default as default };",
16
+ ].join("\n"));
17
+ }
18
+ else {
19
+ buildSync({
20
+ entryPoints: [sourcePath],
21
+ outfile: outputPath,
22
+ bundle: true,
23
+ format: "esm",
24
+ target: ["node18"],
25
+ external: nodeExternals ? nodeExternals.split(",") : [],
26
+ platform: "node",
27
+ banner: {
28
+ js: [
29
+ "import { createRequire as topLevelCreateRequire } from 'module';",
30
+ "const require = topLevelCreateRequire(import.meta.url);",
31
+ "import bannerUrl from 'url';",
32
+ "const __dirname = bannerUrl.fileURLToPath(new URL('.', import.meta.url));",
33
+ ].join(""),
34
+ },
35
+ });
36
+ }
37
+ return outputPath;
38
+ }
39
+ export function compileOpenNextConfigEdge(tempDir, config, openNextConfigPath) {
40
+ const sourcePath = path.join(process.cwd(), openNextConfigPath ?? "open-next.config.ts");
41
+ const outputPath = path.join(tempDir, "open-next.config.edge.mjs");
42
+ // We need to check if the config uses the edge runtime at any point
43
+ // If it does, we need to compile it with the edge runtime
44
+ const usesEdgeRuntime = config.middleware?.external ||
45
+ Object.values(config.functions || {}).some((fn) => fn.runtime === "edge");
46
+ if (!usesEdgeRuntime) {
47
+ logger.debug("No edge runtime found in the open-next.config.ts. Using default config.");
48
+ //Nothing to do here
49
+ }
50
+ else {
51
+ logger.info("Compiling open-next.config.ts for edge runtime.", outputPath);
52
+ buildSync({
53
+ entryPoints: [sourcePath],
54
+ outfile: outputPath,
55
+ bundle: true,
56
+ format: "esm",
57
+ target: ["es2020"],
58
+ conditions: ["worker", "browser"],
59
+ platform: "browser",
60
+ external: config.edgeExternals ?? [],
61
+ });
62
+ logger.info("Compiled open-next.config.ts for edge runtime.");
63
+ }
64
+ }
@@ -0,0 +1 @@
1
+ export declare function copyTracedFiles(buildOutputPath: string, packagePath: string, outputDir: string, routes: string[], bundledNextServer: boolean): Promise<void>;
@@ -0,0 +1,207 @@
1
+ import url from "node:url";
2
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "fs";
3
+ import path from "path";
4
+ import logger from "../logger.js";
5
+ const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
6
+ function copyPatchFile(outputDir) {
7
+ const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js");
8
+ const outputPatchFile = path.join(outputDir, "patchedAsyncStorage.cjs");
9
+ copyFileSync(patchFile, outputPatchFile);
10
+ }
11
+ export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, routes, bundledNextServer) {
12
+ const tsStart = Date.now();
13
+ const dotNextDir = path.join(buildOutputPath, ".next");
14
+ const standaloneDir = path.join(dotNextDir, "standalone");
15
+ const standaloneNextDir = path.join(standaloneDir, packagePath, ".next");
16
+ const outputNextDir = path.join(outputDir, packagePath, ".next");
17
+ const extractFiles = (files, from = standaloneNextDir) => {
18
+ return files.map((f) => path.resolve(from, f));
19
+ };
20
+ // On next 14+, we might not have to include those files
21
+ // For next 13, we need to include them otherwise we get runtime error
22
+ const requiredServerFiles = JSON.parse(readFileSync(path.join(dotNextDir, bundledNextServer
23
+ ? "next-minimal-server.js.nft.json"
24
+ : "next-server.js.nft.json"), "utf8"));
25
+ const filesToCopy = new Map();
26
+ // Files necessary by the server
27
+ extractFiles(requiredServerFiles.files).forEach((f) => {
28
+ filesToCopy.set(f, f.replace(standaloneDir, outputDir));
29
+ });
30
+ // create directory for pages
31
+ if (existsSync(path.join(standaloneDir, ".next/server/pages"))) {
32
+ mkdirSync(path.join(outputNextDir, "server/pages"), {
33
+ recursive: true,
34
+ });
35
+ }
36
+ if (existsSync(path.join(standaloneDir, ".next/server/app"))) {
37
+ mkdirSync(path.join(outputNextDir, "server/app"), {
38
+ recursive: true,
39
+ });
40
+ }
41
+ mkdirSync(path.join(outputNextDir, "server/chunks"), {
42
+ recursive: true,
43
+ });
44
+ const computeCopyFilesForPage = (pagePath) => {
45
+ const fullFilePath = `server/${pagePath}.js`;
46
+ let requiredFiles;
47
+ try {
48
+ requiredFiles = JSON.parse(readFileSync(path.join(standaloneNextDir, `${fullFilePath}.nft.json`), "utf8"));
49
+ }
50
+ catch (e) {
51
+ if (existsSync(path.join(standaloneNextDir, fullFilePath))) {
52
+ //TODO: add a link to the docs
53
+ throw new Error(`
54
+ --------------------------------------------------------------------------------
55
+ ${pagePath} cannot use the edge runtime.
56
+ OpenNext requires edge runtime function to be defined in a separate function.
57
+ See the docs for more information on how to bundle edge runtime functions.
58
+ --------------------------------------------------------------------------------
59
+ `);
60
+ }
61
+ else {
62
+ throw new Error(`
63
+ --------------------------------------------------------------------------------
64
+ We cannot find the route for ${pagePath}.
65
+ File ${fullFilePath} does not exist
66
+ --------------------------------------------------------------------------------`);
67
+ }
68
+ }
69
+ const dir = path.dirname(fullFilePath);
70
+ extractFiles(requiredFiles.files, path.join(standaloneNextDir, dir)).forEach((f) => {
71
+ filesToCopy.set(f, f.replace(standaloneDir, outputDir));
72
+ });
73
+ if (!existsSync(path.join(standaloneNextDir, `${fullFilePath}`))) {
74
+ throw new Error(`This error should only happen for static 404 and 500 page from page router. Report this if that's not the case.,
75
+ File ${fullFilePath} does not exist`);
76
+ }
77
+ filesToCopy.set(path.join(standaloneNextDir, fullFilePath), path.join(outputNextDir, fullFilePath));
78
+ };
79
+ const safeComputeCopyFilesForPage = (pagePath, alternativePath) => {
80
+ try {
81
+ computeCopyFilesForPage(pagePath);
82
+ }
83
+ catch (e) {
84
+ if (alternativePath) {
85
+ computeCopyFilesForPage(alternativePath);
86
+ }
87
+ }
88
+ };
89
+ const hasPageDir = routes.some((route) => route.startsWith("pages/"));
90
+ const hasAppDir = routes.some((route) => route.startsWith("app/"));
91
+ // We need to copy all the base files like _app, _document, _error, etc
92
+ // One thing to note, is that next try to load every routes that might be needed in advance
93
+ // So if you have a [slug].tsx at the root, this route will always be loaded for 1st level request
94
+ // along with _app and _document
95
+ if (hasPageDir) {
96
+ //Page dir
97
+ computeCopyFilesForPage("pages/_app");
98
+ computeCopyFilesForPage("pages/_document");
99
+ computeCopyFilesForPage("pages/_error");
100
+ // These files can be present or not depending on if the user uses getStaticProps
101
+ safeComputeCopyFilesForPage("pages/404");
102
+ safeComputeCopyFilesForPage("pages/500");
103
+ }
104
+ if (hasAppDir) {
105
+ //App dir
106
+ // In next 14.2.0, _not-found is at 'app/_not-found/page'
107
+ safeComputeCopyFilesForPage("app/_not-found", "app/_not-found/page");
108
+ }
109
+ //Files we actually want to include
110
+ routes.forEach((route) => {
111
+ computeCopyFilesForPage(route);
112
+ });
113
+ //Actually copy the files
114
+ filesToCopy.forEach((to, from) => {
115
+ if (
116
+ //TODO: we need to figure which packages we could safely remove
117
+ from.includes(path.join("node_modules", "caniuse-lite")) ||
118
+ // from.includes("jest-worker") || This ones seems necessary for next 12
119
+ from.includes(path.join("node_modules", "sharp"))) {
120
+ return;
121
+ }
122
+ mkdirSync(path.dirname(to), { recursive: true });
123
+ let symlink = null;
124
+ // For pnpm symlink we need to do that
125
+ // see https://github.com/vercel/next.js/blob/498f342b3552d6fc6f1566a1cc5acea324ce0dec/packages/next/src/build/utils.ts#L1932
126
+ try {
127
+ symlink = readlinkSync(from);
128
+ }
129
+ catch (e) {
130
+ //Ignore
131
+ }
132
+ if (symlink) {
133
+ try {
134
+ symlinkSync(symlink, to);
135
+ }
136
+ catch (e) {
137
+ if (e.code !== "EEXIST") {
138
+ throw e;
139
+ }
140
+ }
141
+ }
142
+ else {
143
+ copyFileSync(from, to);
144
+ }
145
+ });
146
+ readdirSync(standaloneNextDir).forEach((f) => {
147
+ if (statSync(path.join(standaloneNextDir, f)).isDirectory())
148
+ return;
149
+ copyFileSync(path.join(standaloneNextDir, f), path.join(outputNextDir, f));
150
+ });
151
+ // We then need to copy all the files at the root of server
152
+ mkdirSync(path.join(outputNextDir, "server"), { recursive: true });
153
+ readdirSync(path.join(standaloneNextDir, "server")).forEach((f) => {
154
+ if (statSync(path.join(standaloneNextDir, "server", f)).isDirectory())
155
+ return;
156
+ if (f !== "server.js") {
157
+ copyFileSync(path.join(standaloneNextDir, "server", f), path.join(path.join(outputNextDir, "server"), f));
158
+ }
159
+ });
160
+ // Copy patch file
161
+ copyPatchFile(path.join(outputDir, packagePath));
162
+ // TODO: Recompute all the files.
163
+ // vercel doesn't seem to do it, but it seems wasteful to have all those files
164
+ // we replace the pages-manifest.json with an empty one if we don't have a pages dir so that
165
+ // next doesn't try to load _app, _document
166
+ if (!hasPageDir) {
167
+ writeFileSync(path.join(outputNextDir, "server/pages-manifest.json"), "{}");
168
+ }
169
+ //TODO: Find what else we need to copy
170
+ const copyStaticFile = (filePath) => {
171
+ if (existsSync(path.join(standaloneNextDir, filePath))) {
172
+ mkdirSync(path.dirname(path.join(outputNextDir, filePath)), {
173
+ recursive: true,
174
+ });
175
+ copyFileSync(path.join(standaloneNextDir, filePath), path.join(outputNextDir, filePath));
176
+ }
177
+ };
178
+ // Get all the static files - Should be only for pages dir
179
+ // Ideally we would filter only those that might get accessed in this specific functions
180
+ // Maybe even move this to s3 directly
181
+ if (hasPageDir) {
182
+ // First we get truly static files - i.e. pages without getStaticProps
183
+ const staticFiles = Object.values(JSON.parse(readFileSync(path.join(standaloneNextDir, "server/pages-manifest.json"), "utf8")));
184
+ // Then we need to get all fallback: true dynamic routes html
185
+ const prerenderManifest = JSON.parse(readFileSync(path.join(standaloneNextDir, "prerender-manifest.json"), "utf8"));
186
+ const config = JSON.parse(readFileSync(path.join(standaloneNextDir, "required-server-files.json"), "utf8")).config;
187
+ const locales = config.i18n?.locales;
188
+ Object.values(prerenderManifest.dynamicRoutes).forEach((route) => {
189
+ if (typeof route.fallback === "string") {
190
+ if (locales) {
191
+ locales.forEach((locale) => {
192
+ staticFiles.push(`pages/${locale}${route.fallback}`);
193
+ });
194
+ }
195
+ else {
196
+ staticFiles.push(`pages${route.fallback}`);
197
+ }
198
+ }
199
+ });
200
+ staticFiles.forEach((f) => {
201
+ if (f.endsWith(".html")) {
202
+ copyStaticFile(`server/${f}`);
203
+ }
204
+ });
205
+ }
206
+ logger.debug("copyTracedFiles:", Date.now() - tsStart, "ms");
207
+ }
@@ -0,0 +1,3 @@
1
+ import { OpenNextConfig } from "../types/open-next";
2
+ import type { BuildOptions } from "./helper.js";
3
+ export declare function createServerBundle(config: OpenNextConfig, options: BuildOptions): Promise<void>;