@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.
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/adapters/binary.d.ts +1 -0
- package/dist/adapters/binary.js +63 -0
- package/dist/adapters/cache.d.ts +96 -0
- package/dist/adapters/cache.js +278 -0
- package/dist/adapters/config/index.d.ts +27 -0
- package/dist/adapters/config/index.js +21 -0
- package/dist/adapters/config/util.d.ts +28 -0
- package/dist/adapters/config/util.js +84 -0
- package/dist/adapters/dynamo-provider.d.ts +8 -0
- package/dist/adapters/dynamo-provider.js +42 -0
- package/dist/adapters/edge-adapter.d.ts +8 -0
- package/dist/adapters/edge-adapter.js +54 -0
- package/dist/adapters/image-optimization-adapter.d.ts +4 -0
- package/dist/adapters/image-optimization-adapter.js +193 -0
- package/dist/adapters/logger.d.ts +13 -0
- package/dist/adapters/logger.js +51 -0
- package/dist/adapters/middleware.d.ts +5 -0
- package/dist/adapters/middleware.js +84 -0
- package/dist/adapters/plugins/14.1/util.d.ts +1 -0
- package/dist/adapters/plugins/14.1/util.js +29 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +11 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.js +14 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +11 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.replacement.js +22 -0
- package/dist/adapters/plugins/without-routing/requestHandler.d.ts +2 -0
- package/dist/adapters/plugins/without-routing/requestHandler.js +10 -0
- package/dist/adapters/revalidate.d.ts +9 -0
- package/dist/adapters/revalidate.js +71 -0
- package/dist/adapters/server-adapter.d.ts +4 -0
- package/dist/adapters/server-adapter.js +27 -0
- package/dist/adapters/util.d.ts +10 -0
- package/dist/adapters/util.js +28 -0
- package/dist/adapters/warmer-function.d.ts +12 -0
- package/dist/adapters/warmer-function.js +80 -0
- package/dist/build/bundleNextServer.d.ts +1 -0
- package/dist/build/bundleNextServer.js +87 -0
- package/dist/build/compileConfig.d.ts +3 -0
- package/dist/build/compileConfig.js +64 -0
- package/dist/build/copyTracedFiles.d.ts +1 -0
- package/dist/build/copyTracedFiles.js +207 -0
- package/dist/build/createServerBundle.d.ts +3 -0
- package/dist/build/createServerBundle.js +228 -0
- package/dist/build/edge/createEdgeBundle.d.ts +20 -0
- package/dist/build/edge/createEdgeBundle.js +149 -0
- package/dist/build/generateOutput.d.ts +2 -0
- package/dist/build/generateOutput.js +239 -0
- package/dist/build/helper.d.ts +33 -0
- package/dist/build/helper.js +193 -0
- package/dist/build/patch/patchedAsyncStorage.d.ts +5 -0
- package/dist/build/patch/patchedAsyncStorage.js +16 -0
- package/dist/build/validateConfig.d.ts +2 -0
- package/dist/build/validateConfig.js +76 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +533 -0
- package/dist/cache/incremental/s3-lite.d.ts +3 -0
- package/dist/cache/incremental/s3-lite.js +72 -0
- package/dist/cache/incremental/s3.d.ts +3 -0
- package/dist/cache/incremental/s3.js +44 -0
- package/dist/cache/incremental/types.d.ts +34 -0
- package/dist/cache/incremental/types.js +1 -0
- package/dist/cache/next-types.d.ts +62 -0
- package/dist/cache/next-types.js +1 -0
- package/dist/cache/tag/constants.d.ts +2 -0
- package/dist/cache/tag/constants.js +17 -0
- package/dist/cache/tag/dynamodb-lite.d.ts +3 -0
- package/dist/cache/tag/dynamodb-lite.js +174 -0
- package/dist/cache/tag/dynamodb.d.ts +3 -0
- package/dist/cache/tag/dynamodb.js +129 -0
- package/dist/cache/tag/types.d.ts +11 -0
- package/dist/cache/tag/types.js +1 -0
- package/dist/converters/aws-apigw-v1.d.ts +3 -0
- package/dist/converters/aws-apigw-v1.js +89 -0
- package/dist/converters/aws-apigw-v2.d.ts +3 -0
- package/dist/converters/aws-apigw-v2.js +99 -0
- package/dist/converters/aws-cloudfront.d.ts +3 -0
- package/dist/converters/aws-cloudfront.js +170 -0
- package/dist/converters/dummy.d.ts +7 -0
- package/dist/converters/dummy.js +16 -0
- package/dist/converters/edge.d.ts +6 -0
- package/dist/converters/edge.js +88 -0
- package/dist/converters/node.d.ts +3 -0
- package/dist/converters/node.js +45 -0
- package/dist/converters/sqs-revalidate.d.ts +4 -0
- package/dist/converters/sqs-revalidate.js +22 -0
- package/dist/converters/utils.d.ts +1 -0
- package/dist/converters/utils.js +9 -0
- package/dist/core/createGenericHandler.d.ts +11 -0
- package/dist/core/createGenericHandler.js +16 -0
- package/dist/core/createMainHandler.d.ts +18 -0
- package/dist/core/createMainHandler.js +24 -0
- package/dist/core/edgeFunctionHandler.d.ts +54 -0
- package/dist/core/edgeFunctionHandler.js +21 -0
- package/dist/core/patchAsyncStorage.d.ts +1 -0
- package/dist/core/patchAsyncStorage.js +16 -0
- package/dist/core/requestHandler.d.ts +3 -0
- package/dist/core/requestHandler.js +162 -0
- package/dist/core/require-hooks.d.ts +3 -0
- package/dist/core/require-hooks.js +102 -0
- package/dist/core/resolve.d.ts +31 -0
- package/dist/core/resolve.js +81 -0
- package/dist/core/routing/cacheInterceptor.d.ts +2 -0
- package/dist/core/routing/cacheInterceptor.js +162 -0
- package/dist/core/routing/i18n/accept-header.d.ts +1 -0
- package/dist/core/routing/i18n/accept-header.js +101 -0
- package/dist/core/routing/i18n/index.d.ts +2 -0
- package/dist/core/routing/i18n/index.js +36 -0
- package/dist/core/routing/matcher.d.ts +25 -0
- package/dist/core/routing/matcher.js +294 -0
- package/dist/core/routing/middleware.d.ts +7 -0
- package/dist/core/routing/middleware.js +167 -0
- package/dist/core/routing/util.d.ts +105 -0
- package/dist/core/routing/util.js +415 -0
- package/dist/core/routingHandler.d.ts +8 -0
- package/dist/core/routingHandler.js +127 -0
- package/dist/core/util.d.ts +5 -0
- package/dist/core/util.js +84 -0
- package/dist/helpers/withCloudflare.d.ts +65 -0
- package/dist/helpers/withCloudflare.js +58 -0
- package/dist/helpers/withSST.d.ts +54 -0
- package/dist/helpers/withSST.js +28 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +4 -0
- package/dist/http/openNextResponse.d.ts +78 -0
- package/dist/http/openNextResponse.js +281 -0
- package/dist/http/request.d.ts +12 -0
- package/dist/http/request.js +35 -0
- package/dist/http/util.d.ts +4 -0
- package/dist/http/util.js +34 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +37 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +13 -0
- package/dist/minimize-js.d.ts +1 -0
- package/dist/minimize-js.js +101 -0
- package/dist/overrides/imageLoader/host.d.ts +3 -0
- package/dist/overrides/imageLoader/host.js +29 -0
- package/dist/overrides/imageLoader/s3.d.ts +3 -0
- package/dist/overrides/imageLoader/s3.js +33 -0
- package/dist/plugins/edge.d.ts +15 -0
- package/dist/plugins/edge.js +157 -0
- package/dist/plugins/replacement.d.ts +42 -0
- package/dist/plugins/replacement.js +74 -0
- package/dist/plugins/resolve.d.ts +18 -0
- package/dist/plugins/resolve.js +45 -0
- package/dist/queue/sqs-lite.d.ts +3 -0
- package/dist/queue/sqs-lite.js +52 -0
- package/dist/queue/sqs.d.ts +3 -0
- package/dist/queue/sqs.js +20 -0
- package/dist/queue/types.d.ts +12 -0
- package/dist/queue/types.js +1 -0
- package/dist/types/aws-lambda.d.ts +22 -0
- package/dist/types/aws-lambda.js +1 -0
- package/dist/types/next-types.d.ts +155 -0
- package/dist/types/next-types.js +2 -0
- package/dist/types/open-next.d.ts +303 -0
- package/dist/types/open-next.js +1 -0
- package/dist/utils/error.d.ts +23 -0
- package/dist/utils/error.js +32 -0
- package/dist/utils/fetch.d.ts +2 -0
- package/dist/utils/fetch.js +23 -0
- package/dist/utils/promise.d.ts +18 -0
- package/dist/utils/promise.js +48 -0
- package/dist/utils/stream.d.ts +5 -0
- package/dist/utils/stream.js +30 -0
- package/dist/wrappers/aws-lambda-streaming.d.ts +7 -0
- package/dist/wrappers/aws-lambda-streaming.js +88 -0
- package/dist/wrappers/aws-lambda.d.ts +7 -0
- package/dist/wrappers/aws-lambda.js +39 -0
- package/dist/wrappers/cloudflare.d.ts +11 -0
- package/dist/wrappers/cloudflare.js +20 -0
- package/dist/wrappers/node.d.ts +7 -0
- package/dist/wrappers/node.js +57 -0
- package/package.json +66 -0
|
@@ -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,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
|
+
}
|