@opennextjs/cloudflare 0.2.1 → 0.3.0
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/README.md +48 -38
- package/dist/api/{get-cloudflare-context.d.mts → get-cloudflare-context.d.ts} +4 -4
- package/dist/api/get-cloudflare-context.js +39 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/kvCache.d.ts +27 -0
- package/dist/api/kvCache.js +121 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +48 -0
- package/dist/cli/build/bundle-server.d.ts +6 -0
- package/dist/cli/build/bundle-server.js +188 -0
- package/dist/cli/build/index.d.ts +9 -0
- package/dist/cli/build/index.js +123 -0
- package/dist/cli/build/open-next/compile-env-files.d.ts +5 -0
- package/dist/cli/build/open-next/compile-env-files.js +9 -0
- package/dist/cli/build/open-next/copyCacheAssets.d.ts +2 -0
- package/dist/cli/build/open-next/copyCacheAssets.js +10 -0
- package/dist/cli/build/open-next/createServerBundle.d.ts +2 -0
- package/dist/cli/build/open-next/createServerBundle.js +216 -0
- package/dist/cli/build/patches/index.d.ts +2 -0
- package/dist/cli/build/patches/index.js +2 -0
- package/dist/cli/build/patches/investigated/copy-package-cli-files.d.ts +6 -0
- package/dist/cli/build/patches/investigated/copy-package-cli-files.js +12 -0
- package/dist/cli/build/patches/investigated/index.d.ts +4 -0
- package/dist/cli/build/patches/investigated/index.js +4 -0
- package/dist/cli/build/patches/investigated/patch-cache.d.ts +13 -0
- package/dist/cli/build/patches/investigated/patch-cache.js +22 -0
- package/dist/cli/build/patches/investigated/patch-require.d.ts +7 -0
- package/dist/cli/build/patches/investigated/patch-require.js +9 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.d.ts +13 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.js +82 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.d.ts +1 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.js +20 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.d.ts +19 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.js +76 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.d.ts +1 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.js +23 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.d.ts +14 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.js +22 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.d.ts +1 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.js +15 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.d.ts +8 -0
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.js +22 -0
- package/dist/cli/build/patches/to-investigate/index.d.ts +8 -0
- package/dist/cli/build/patches/to-investigate/index.js +8 -0
- package/dist/cli/build/patches/to-investigate/inline-eval-manifest.d.ts +9 -0
- package/dist/cli/build/patches/to-investigate/inline-eval-manifest.js +32 -0
- package/dist/cli/build/patches/to-investigate/inline-middleware-manifest-require.d.ts +6 -0
- package/dist/cli/build/patches/to-investigate/inline-middleware-manifest-require.js +13 -0
- package/dist/cli/build/patches/to-investigate/inline-next-require.d.ts +6 -0
- package/dist/cli/build/patches/to-investigate/inline-next-require.js +36 -0
- package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.d.ts +7 -0
- package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.js +9 -0
- package/dist/cli/build/patches/to-investigate/patch-find-dir.d.ts +8 -0
- package/dist/cli/build/patches/to-investigate/patch-find-dir.js +21 -0
- package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.d.ts +14 -0
- package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.js +34 -0
- package/dist/cli/build/patches/to-investigate/patch-read-file.d.ts +3 -0
- package/dist/cli/build/patches/to-investigate/patch-read-file.js +29 -0
- package/dist/cli/build/patches/to-investigate/wrangler-deps.d.ts +2 -0
- package/dist/cli/build/patches/to-investigate/wrangler-deps.js +54 -0
- package/dist/cli/build/utils/extract-project-env-vars.d.ts +18 -0
- package/dist/cli/build/utils/extract-project-env-vars.js +32 -0
- package/dist/cli/build/utils/extract-project-env-vars.spec.d.ts +1 -0
- package/dist/cli/build/utils/extract-project-env-vars.spec.js +57 -0
- package/dist/cli/build/utils/index.d.ts +3 -0
- package/dist/cli/build/utils/index.js +3 -0
- package/dist/cli/build/utils/normalize-path.d.ts +1 -0
- package/dist/cli/build/utils/normalize-path.js +4 -0
- package/dist/cli/build/utils/read-paths-recursively.d.ts +7 -0
- package/dist/cli/build/utils/read-paths-recursively.js +20 -0
- package/dist/cli/build/utils/ts-parse-file.d.ts +8 -0
- package/dist/cli/build/utils/ts-parse-file.js +12 -0
- package/dist/cli/config.d.ts +41 -0
- package/dist/cli/config.js +92 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/templates/shims/empty.d.ts +2 -0
- package/dist/cli/templates/shims/env.d.ts +1 -0
- package/dist/cli/templates/shims/env.js +1 -0
- package/dist/cli/templates/shims/node-fs.d.ts +17 -0
- package/dist/cli/templates/shims/node-fs.js +51 -0
- package/dist/cli/templates/shims/throw.d.ts +0 -0
- package/dist/cli/templates/shims/{throw.ts → throw.js} +1 -0
- package/dist/cli/templates/worker.d.ts +5 -0
- package/dist/cli/templates/worker.js +67 -0
- package/package.json +29 -12
- package/dist/api/chunk-VTBEIZPQ.mjs +0 -32
- package/dist/api/get-cloudflare-context.mjs +0 -6
- package/dist/api/index.d.mts +0 -1
- package/dist/api/index.mjs +0 -6
- package/dist/cli/constants/incremental-cache.ts +0 -8
- package/dist/cli/index.mjs +0 -7422
- package/dist/cli/templates/cache-handler/index.ts +0 -1
- package/dist/cli/templates/cache-handler/open-next-cache-handler.ts +0 -148
- package/dist/cli/templates/cache-handler/utils.ts +0 -41
- package/dist/cli/templates/shims/env.ts +0 -1
- package/dist/cli/templates/shims/node-fs.ts +0 -69
- package/dist/cli/templates/worker.ts +0 -156
- /package/dist/cli/templates/shims/{empty.ts → empty.js} +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as ts from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Updates the function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is a function that
|
|
4
|
+
* installs chunks by importing/requiring them at runtime.
|
|
5
|
+
*
|
|
6
|
+
* `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
|
|
7
|
+
*
|
|
8
|
+
* This function needs to be updated so that it requires chunks using the standard `require` function and not webpack's custom `require` logic
|
|
9
|
+
* which fails in the workerd runtime.
|
|
10
|
+
*
|
|
11
|
+
* @param sourceFile the webpack runtime file parsed with ts-morph (note: this gets side-effectfully updated)
|
|
12
|
+
* @param chunkInstallationIdentifiers the names of the `installedChunks` and `installChunk` variables
|
|
13
|
+
* @param chunks the identifiers of the chunks (found on the filesystem)
|
|
14
|
+
* @returns the content of the sourceFile but with the require function updated
|
|
15
|
+
*/
|
|
16
|
+
export async function getFileContentWithUpdatedWebpackFRequireCode(sourceFile, { installedChunks, installChunk }, chunks) {
|
|
17
|
+
const webpackFRequireFunction = sourceFile
|
|
18
|
+
.getDescendantsOfKind(ts.SyntaxKind.ArrowFunction)
|
|
19
|
+
.find((arrowFunction) => {
|
|
20
|
+
// the function is declared assigned in a binary expression, so let's check the binary expression
|
|
21
|
+
// to get more confidence that we've got the right arrow function
|
|
22
|
+
const binaryExpression = arrowFunction.getFirstAncestorByKind(ts.SyntaxKind.BinaryExpression);
|
|
23
|
+
if (!binaryExpression)
|
|
24
|
+
return false;
|
|
25
|
+
// the variable being assigned always ends with .f.require (even in unminified files)
|
|
26
|
+
const binaryExpressionLeft = binaryExpression.getLeft();
|
|
27
|
+
if (!binaryExpressionLeft.getText().endsWith(".f.require"))
|
|
28
|
+
return false;
|
|
29
|
+
// the binary expression is an assignment
|
|
30
|
+
const binaryExpressionOperator = binaryExpression.getOperatorToken();
|
|
31
|
+
if (binaryExpressionOperator.getText() !== "=")
|
|
32
|
+
return false;
|
|
33
|
+
// the right side of the expression is our arrow function
|
|
34
|
+
const binaryExpressionRight = binaryExpression.getRight();
|
|
35
|
+
if (binaryExpressionRight !== arrowFunction)
|
|
36
|
+
return false;
|
|
37
|
+
const arrowFunctionBody = arrowFunction.getBody();
|
|
38
|
+
if (!arrowFunctionBody.isKind(ts.SyntaxKind.Block))
|
|
39
|
+
return false;
|
|
40
|
+
const arrowFunctionBodyText = arrowFunctionBody.getText();
|
|
41
|
+
const functionUsesChunkInstallationVariables = arrowFunctionBodyText.includes(installChunk) && arrowFunctionBodyText.includes(installedChunks);
|
|
42
|
+
if (!functionUsesChunkInstallationVariables)
|
|
43
|
+
return false;
|
|
44
|
+
const functionParameters = arrowFunction.getParameters();
|
|
45
|
+
if (functionParameters.length !== 2)
|
|
46
|
+
return false;
|
|
47
|
+
const callsInstallChunk = arrowFunctionBody
|
|
48
|
+
.getDescendantsOfKind(ts.SyntaxKind.CallExpression)
|
|
49
|
+
.some((callExpression) => callExpression.getExpression().getText() === installChunk);
|
|
50
|
+
if (!callsInstallChunk)
|
|
51
|
+
return false;
|
|
52
|
+
const functionFirstParameterName = functionParameters[0]?.getName();
|
|
53
|
+
const accessesInstalledChunksUsingItsFirstParameter = arrowFunctionBody
|
|
54
|
+
.getDescendantsOfKind(ts.SyntaxKind.ElementAccessExpression)
|
|
55
|
+
.some((elementAccess) => {
|
|
56
|
+
return (elementAccess.getExpression().getText() === installedChunks &&
|
|
57
|
+
elementAccess.getArgumentExpression()?.getText() === functionFirstParameterName);
|
|
58
|
+
});
|
|
59
|
+
if (!accessesInstalledChunksUsingItsFirstParameter)
|
|
60
|
+
return false;
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
if (!webpackFRequireFunction) {
|
|
64
|
+
throw new Error("ERROR: unable to find the webpack f require function declaration");
|
|
65
|
+
}
|
|
66
|
+
const functionParameterNames = webpackFRequireFunction
|
|
67
|
+
.getParameters()
|
|
68
|
+
.map((parameter) => parameter.getName());
|
|
69
|
+
const chunkId = functionParameterNames[0];
|
|
70
|
+
const functionBody = webpackFRequireFunction.getBody();
|
|
71
|
+
functionBody.insertStatements(0, [
|
|
72
|
+
`if (${installedChunks}[${chunkId}]) return;`,
|
|
73
|
+
...chunks.map((chunk) => `\nif(${chunkId} === ${chunk}) return ${installChunk}(require("./chunks/${chunk}.js"));`),
|
|
74
|
+
]);
|
|
75
|
+
return sourceFile.print();
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { tsParseFile } from "../../../utils/index.js";
|
|
4
|
+
import { getFileContentWithUpdatedWebpackFRequireCode } from "./get-file-content-with-updated-webpack-f-require-code";
|
|
5
|
+
describe("getFileContentWithUpdatedWebpackFRequireCode", () => {
|
|
6
|
+
test("returns the updated content of the f.require function from unminified webpack runtime code", async () => {
|
|
7
|
+
const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, "utf8");
|
|
8
|
+
const tsSourceFile = tsParseFile(fileContent);
|
|
9
|
+
const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode(tsSourceFile, { installChunk: "installChunk", installedChunks: "installedChunks" }, ["658"]);
|
|
10
|
+
expect(unstyleCode(updatedFCode)).toContain(`if (installedChunks[chunkId]) return;`);
|
|
11
|
+
expect(unstyleCode(updatedFCode)).toContain(`if (chunkId === 658) return installChunk(require("./chunks/658.js"));`);
|
|
12
|
+
});
|
|
13
|
+
test("returns the updated content of the f.require function from minified webpack runtime code", async () => {
|
|
14
|
+
const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, "utf8");
|
|
15
|
+
const tsSourceFile = tsParseFile(fileContent);
|
|
16
|
+
const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode(tsSourceFile, { installChunk: "r", installedChunks: "e" }, ["658"]);
|
|
17
|
+
expect(unstyleCode(updatedFCode)).toContain("if (e[o]) return;");
|
|
18
|
+
expect(unstyleCode(updatedFCode)).toContain(`if (o === 658) return r(require("./chunks/658.js"));`);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
function unstyleCode(text) {
|
|
22
|
+
return text.replace(/\n\s+/g, "\n").replace(/\n/g, " ");
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Updates the content of the webpack runtime file in a manner so that it doesn't perform runtime dynamic `require` calls which fail in our runtime.
|
|
3
|
+
*
|
|
4
|
+
* It does so by appropriately updating a function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is
|
|
5
|
+
* the one that normally would cause dynamic requires to happen at runtime.
|
|
6
|
+
*
|
|
7
|
+
* `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* @param fileContent the content of the webpack runtime file
|
|
11
|
+
* @param chunks the identifiers of the chunks (found on the filesystem)
|
|
12
|
+
* @returns the content of the webpack runtime file updated with our custom logic
|
|
13
|
+
*/
|
|
14
|
+
export declare function getUpdatedWebpackChunksFileContent(fileContent: string, chunks: string[]): Promise<string>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { tsParseFile } from "../../../utils/index.js";
|
|
2
|
+
import { getChunkInstallationIdentifiers } from "./get-chunk-installation-identifiers.js";
|
|
3
|
+
import { getFileContentWithUpdatedWebpackFRequireCode } from "./get-file-content-with-updated-webpack-f-require-code.js";
|
|
4
|
+
/**
|
|
5
|
+
* Updates the content of the webpack runtime file in a manner so that it doesn't perform runtime dynamic `require` calls which fail in our runtime.
|
|
6
|
+
*
|
|
7
|
+
* It does so by appropriately updating a function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is
|
|
8
|
+
* the one that normally would cause dynamic requires to happen at runtime.
|
|
9
|
+
*
|
|
10
|
+
* `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
|
|
11
|
+
*
|
|
12
|
+
*
|
|
13
|
+
* @param fileContent the content of the webpack runtime file
|
|
14
|
+
* @param chunks the identifiers of the chunks (found on the filesystem)
|
|
15
|
+
* @returns the content of the webpack runtime file updated with our custom logic
|
|
16
|
+
*/
|
|
17
|
+
export async function getUpdatedWebpackChunksFileContent(fileContent, chunks) {
|
|
18
|
+
const tsSourceFile = tsParseFile(fileContent);
|
|
19
|
+
const chunkInstallationIdentifiers = await getChunkInstallationIdentifiers(tsSourceFile);
|
|
20
|
+
const updatedFileContent = getFileContentWithUpdatedWebpackFRequireCode(tsSourceFile, chunkInstallationIdentifiers, chunks);
|
|
21
|
+
return updatedFileContent;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { getUpdatedWebpackChunksFileContent } from "./get-updated-webpack-chunks-file-content.js";
|
|
4
|
+
describe("getUpdatedWebpackChunksFileContent", () => {
|
|
5
|
+
test("returns the updated content of a webpack runtime chunks unminified file", async () => {
|
|
6
|
+
const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, "utf8");
|
|
7
|
+
const updatedContent = await getUpdatedWebpackChunksFileContent(fileContent, ["658"]);
|
|
8
|
+
expect(updatedContent).toMatchFileSnapshot("./test-snapshots/unminified-webpacks-file.js");
|
|
9
|
+
});
|
|
10
|
+
test("returns the updated content of a webpack runtime chunks minified file", async () => {
|
|
11
|
+
const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, "utf8");
|
|
12
|
+
const updatedContent = await getUpdatedWebpackChunksFileContent(fileContent, ["658"]);
|
|
13
|
+
expect(updatedContent).toMatchFileSnapshot("./test-snapshots/minified-webpacks-file.js");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config } from "../../../../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fixes the webpack-runtime.js file by removing its webpack dynamic requires.
|
|
4
|
+
*
|
|
5
|
+
* This hack is particularly bad as it indicates that files inside the output directory still get a hold of files from the outside: `${nextjsAppPaths.standaloneAppServerDir}/webpack-runtime.js`
|
|
6
|
+
* so this shows that not everything that's needed to deploy the application is in the output directory...
|
|
7
|
+
*/
|
|
8
|
+
export declare function updateWebpackChunksFile(config: Config): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getUpdatedWebpackChunksFileContent } from "./get-updated-webpack-chunks-file-content.js";
|
|
4
|
+
/**
|
|
5
|
+
* Fixes the webpack-runtime.js file by removing its webpack dynamic requires.
|
|
6
|
+
*
|
|
7
|
+
* This hack is particularly bad as it indicates that files inside the output directory still get a hold of files from the outside: `${nextjsAppPaths.standaloneAppServerDir}/webpack-runtime.js`
|
|
8
|
+
* so this shows that not everything that's needed to deploy the application is in the output directory...
|
|
9
|
+
*/
|
|
10
|
+
export async function updateWebpackChunksFile(config) {
|
|
11
|
+
console.log("# updateWebpackChunksFile");
|
|
12
|
+
const webpackRuntimeFile = join(config.paths.output.standaloneAppServer, "webpack-runtime.js");
|
|
13
|
+
const fileContent = readFileSync(webpackRuntimeFile, "utf-8");
|
|
14
|
+
const chunks = readdirSync(join(config.paths.output.standaloneAppServer, "chunks"))
|
|
15
|
+
.filter((chunk) => /^\d+\.js$/.test(chunk))
|
|
16
|
+
.map((chunk) => {
|
|
17
|
+
console.log(` - chunk ${chunk}`);
|
|
18
|
+
return chunk.replace(/\.js$/, "");
|
|
19
|
+
});
|
|
20
|
+
const updatedFileContent = await getUpdatedWebpackChunksFileContent(fileContent, chunks);
|
|
21
|
+
writeFileSync(webpackRuntimeFile, updatedFileContent);
|
|
22
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./inline-eval-manifest.js";
|
|
2
|
+
export * from "./inline-middleware-manifest-require.js";
|
|
3
|
+
export * from "./inline-next-require.js";
|
|
4
|
+
export * from "./patch-exception-bubbling.js";
|
|
5
|
+
export * from "./patch-find-dir.js";
|
|
6
|
+
export * from "./patch-load-instrumentation-module.js";
|
|
7
|
+
export * from "./patch-read-file.js";
|
|
8
|
+
export * from "./wrangler-deps.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./inline-eval-manifest.js";
|
|
2
|
+
export * from "./inline-middleware-manifest-require.js";
|
|
3
|
+
export * from "./inline-next-require.js";
|
|
4
|
+
export * from "./patch-exception-bubbling.js";
|
|
5
|
+
export * from "./patch-find-dir.js";
|
|
6
|
+
export * from "./patch-load-instrumentation-module.js";
|
|
7
|
+
export * from "./patch-read-file.js";
|
|
8
|
+
export * from "./wrangler-deps.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Config } from "../../../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* `evalManifest` relies on readFileSync so we need to patch the function so that it instead returns the content of the manifest files
|
|
4
|
+
* which are known at build time
|
|
5
|
+
* (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L72)
|
|
6
|
+
* Note: we could/should probably just patch readFileSync here or something, but here the issue is that after the readFileSync call
|
|
7
|
+
* there is a vm `runInNewContext` call which we also don't support (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L88)
|
|
8
|
+
*/
|
|
9
|
+
export declare function inlineEvalManifest(code: string, config: Config): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { join, posix } from "node:path";
|
|
2
|
+
import { globSync } from "glob";
|
|
3
|
+
import { normalizePath } from "../../utils/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* `evalManifest` relies on readFileSync so we need to patch the function so that it instead returns the content of the manifest files
|
|
6
|
+
* which are known at build time
|
|
7
|
+
* (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L72)
|
|
8
|
+
* Note: we could/should probably just patch readFileSync here or something, but here the issue is that after the readFileSync call
|
|
9
|
+
* there is a vm `runInNewContext` call which we also don't support (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L88)
|
|
10
|
+
*/
|
|
11
|
+
export function inlineEvalManifest(code, config) {
|
|
12
|
+
const manifestJss = globSync(normalizePath(join(config.paths.output.standaloneAppDotNext, "**", "*_client-reference-manifest.js"))).map((file) => normalizePath(file).replace(normalizePath(config.paths.output.standaloneApp) + posix.sep, ""));
|
|
13
|
+
return code.replace(/function evalManifest\((.+?), .+?\) {/, `$&
|
|
14
|
+
${manifestJss
|
|
15
|
+
.map((manifestJs) => `
|
|
16
|
+
if ($1.endsWith("${manifestJs}")) {
|
|
17
|
+
require(${JSON.stringify(join(config.paths.output.standaloneApp, manifestJs))});
|
|
18
|
+
return {
|
|
19
|
+
__RSC_MANIFEST: {
|
|
20
|
+
"${manifestJs
|
|
21
|
+
.replace(".next/server/app", "")
|
|
22
|
+
.replace("_client-reference-manifest.js", "")}": globalThis.__RSC_MANIFEST["${manifestJs
|
|
23
|
+
.replace(".next/server/app", "")
|
|
24
|
+
.replace("_client-reference-manifest.js", "")}"],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
`)
|
|
29
|
+
.join("\n")}
|
|
30
|
+
throw new Error("Unknown evalManifest: " + $1);
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Config } from "../../../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Inlines the middleware manifest from the build output to prevent a dynamic require statement
|
|
4
|
+
* as they result in runtime failures.
|
|
5
|
+
*/
|
|
6
|
+
export declare function inlineMiddlewareManifestRequire(code: string, config: Config): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Inlines the middleware manifest from the build output to prevent a dynamic require statement
|
|
5
|
+
* as they result in runtime failures.
|
|
6
|
+
*/
|
|
7
|
+
export function inlineMiddlewareManifestRequire(code, config) {
|
|
8
|
+
const middlewareManifestPath = join(config.paths.output.standaloneAppServer, "middleware-manifest.json");
|
|
9
|
+
const middlewareManifest = existsSync(middlewareManifestPath)
|
|
10
|
+
? JSON.parse(readFileSync(middlewareManifestPath, "utf-8"))
|
|
11
|
+
: {};
|
|
12
|
+
return code.replace("require(this.middlewareManifestPath)", JSON.stringify(middlewareManifest));
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Config } from "../../../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* The following avoid various Next.js specific files `require`d at runtime since we can just read
|
|
4
|
+
* and inline their content during build time
|
|
5
|
+
*/
|
|
6
|
+
export declare function inlineNextRequire(code: string, config: Config): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* The following avoid various Next.js specific files `require`d at runtime since we can just read
|
|
5
|
+
* and inline their content during build time
|
|
6
|
+
*/
|
|
7
|
+
export function inlineNextRequire(code, config) {
|
|
8
|
+
const pagesManifestFile = join(config.paths.output.standaloneAppServer, "pages-manifest.json");
|
|
9
|
+
const appPathsManifestFile = join(config.paths.output.standaloneAppServer, "app-paths-manifest.json");
|
|
10
|
+
const pagesManifestFiles = existsSync(pagesManifestFile)
|
|
11
|
+
? Object.values(JSON.parse(readFileSync(pagesManifestFile, "utf-8"))).map((file) => ".next/server/" + file)
|
|
12
|
+
: [];
|
|
13
|
+
const appPathsManifestFiles = existsSync(appPathsManifestFile)
|
|
14
|
+
? Object.values(JSON.parse(readFileSync(appPathsManifestFile, "utf-8"))).map((file) => ".next/server/" + file)
|
|
15
|
+
: [];
|
|
16
|
+
const allManifestFiles = pagesManifestFiles.concat(appPathsManifestFiles);
|
|
17
|
+
const htmlPages = allManifestFiles.filter((file) => file.endsWith(".html"));
|
|
18
|
+
const pageModules = allManifestFiles.filter((file) => file.endsWith(".js"));
|
|
19
|
+
return code.replace(/const pagePath = getPagePath\(.+?\);/, `$&
|
|
20
|
+
${htmlPages
|
|
21
|
+
.map((htmlPage) => `
|
|
22
|
+
if (pagePath.endsWith("${htmlPage}")) {
|
|
23
|
+
return ${JSON.stringify(readFileSync(join(config.paths.output.standaloneApp, htmlPage), "utf-8"))};
|
|
24
|
+
}
|
|
25
|
+
`)
|
|
26
|
+
.join("\n")}
|
|
27
|
+
${pageModules
|
|
28
|
+
.map((module) => `
|
|
29
|
+
if (pagePath.endsWith("${module}")) {
|
|
30
|
+
return require(${JSON.stringify(join(config.paths.output.standaloneApp, module))});
|
|
31
|
+
}
|
|
32
|
+
`)
|
|
33
|
+
.join("\n")}
|
|
34
|
+
throw new Error("Unknown pagePath: " + pagePath);
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When using SSG and `dynamicParams = false`, Next.js throws a NoFallbackError. This error is
|
|
3
|
+
* bubbled up by default in Node.js servers, however this causes issues in the workerd with
|
|
4
|
+
* the current response handling and streaming implementation we have, and leads to hanging
|
|
5
|
+
* promises.
|
|
6
|
+
*/
|
|
7
|
+
export declare function patchExceptionBubbling(code: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When using SSG and `dynamicParams = false`, Next.js throws a NoFallbackError. This error is
|
|
3
|
+
* bubbled up by default in Node.js servers, however this causes issues in the workerd with
|
|
4
|
+
* the current response handling and streaming implementation we have, and leads to hanging
|
|
5
|
+
* promises.
|
|
6
|
+
*/
|
|
7
|
+
export function patchExceptionBubbling(code) {
|
|
8
|
+
return code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config } from "../../../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Here we patch `findDir` so that the next server can detect whether the `app` or `pages` directory exists
|
|
4
|
+
* (source: https://github.com/vercel/next.js/blob/ba995993/packages/next/src/lib/find-pages-dir.ts#L4-L13)
|
|
5
|
+
* (usage source: https://github.com/vercel/next.js/blob/ba995993/packages/next/src/server/next-server.ts#L450-L451)
|
|
6
|
+
* Note: `findDir` uses `fs.existsSync` under the hood, so patching that should be enough to make this work
|
|
7
|
+
*/
|
|
8
|
+
export declare function patchFindDir(code: string, config: Config): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Here we patch `findDir` so that the next server can detect whether the `app` or `pages` directory exists
|
|
5
|
+
* (source: https://github.com/vercel/next.js/blob/ba995993/packages/next/src/lib/find-pages-dir.ts#L4-L13)
|
|
6
|
+
* (usage source: https://github.com/vercel/next.js/blob/ba995993/packages/next/src/server/next-server.ts#L450-L451)
|
|
7
|
+
* Note: `findDir` uses `fs.existsSync` under the hood, so patching that should be enough to make this work
|
|
8
|
+
*/
|
|
9
|
+
export function patchFindDir(code, config) {
|
|
10
|
+
return code.replace(/function findDir\((?<dir>dir\d*), (?<name>name\d*)\) {/, `function findDir($dir, $name) {
|
|
11
|
+
if ($dir.endsWith(".next/server")) {
|
|
12
|
+
if ($name === "app") {
|
|
13
|
+
return ${existsSync(`${join(config.paths.output.standaloneAppServer, "app")}`)};
|
|
14
|
+
}
|
|
15
|
+
if ($name === "pages") {
|
|
16
|
+
return ${existsSync(`${join(config.paths.output.standaloneAppServer, "pages")}`)};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw new Error("Unknown findDir call: " + $dir + " " + $name);
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `loadInstrumentationModule` method (source: https://github.com/vercel/next.js/blob/5b7833e3/packages/next/src/server/next-server.ts#L301)
|
|
3
|
+
* calls `module.findSourceMap` (https://nodejs.org/api/module.html#modulefindsourcemappath) which we haven't implemented causing a runtime error.
|
|
4
|
+
*
|
|
5
|
+
* To solve this issue this function gets all the `loadInstrumentationModule` declarations found in the file and removes all the statements
|
|
6
|
+
* from their bodies (making them no-op methods).
|
|
7
|
+
*
|
|
8
|
+
* Instrumentation is a Next.js feature for monitoring and logging (see: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation),
|
|
9
|
+
* the removal of this method's logic most likely breaks this feature (see: https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation),
|
|
10
|
+
* so this function is likely temporary and something that we'll have to fix in the future.
|
|
11
|
+
*
|
|
12
|
+
* TODO: investigate and re-enable instrumentation (https://github.com/opennextjs/opennextjs-cloudflare/issues/171)
|
|
13
|
+
*/
|
|
14
|
+
export declare function patchLoadInstrumentationModule(code: string): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as ts from "ts-morph";
|
|
2
|
+
import { tsParseFile } from "../../utils/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `loadInstrumentationModule` method (source: https://github.com/vercel/next.js/blob/5b7833e3/packages/next/src/server/next-server.ts#L301)
|
|
5
|
+
* calls `module.findSourceMap` (https://nodejs.org/api/module.html#modulefindsourcemappath) which we haven't implemented causing a runtime error.
|
|
6
|
+
*
|
|
7
|
+
* To solve this issue this function gets all the `loadInstrumentationModule` declarations found in the file and removes all the statements
|
|
8
|
+
* from their bodies (making them no-op methods).
|
|
9
|
+
*
|
|
10
|
+
* Instrumentation is a Next.js feature for monitoring and logging (see: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation),
|
|
11
|
+
* the removal of this method's logic most likely breaks this feature (see: https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation),
|
|
12
|
+
* so this function is likely temporary and something that we'll have to fix in the future.
|
|
13
|
+
*
|
|
14
|
+
* TODO: investigate and re-enable instrumentation (https://github.com/opennextjs/opennextjs-cloudflare/issues/171)
|
|
15
|
+
*/
|
|
16
|
+
export function patchLoadInstrumentationModule(code) {
|
|
17
|
+
const file = tsParseFile(code);
|
|
18
|
+
const loadInstrumentationModuleDeclarations = file
|
|
19
|
+
.getDescendantsOfKind(ts.SyntaxKind.MethodDeclaration)
|
|
20
|
+
.filter((methodDeclaration) => {
|
|
21
|
+
if (methodDeclaration.getName() !== "loadInstrumentationModule") {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const methodModifierKinds = methodDeclaration.getModifiers().map((modifier) => modifier.getKind());
|
|
25
|
+
if (methodModifierKinds.length !== 1 || methodModifierKinds[0] !== ts.SyntaxKind.AsyncKeyword) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
loadInstrumentationModuleDeclarations.forEach((loadInstrumentationModuleDeclaration) => {
|
|
31
|
+
loadInstrumentationModuleDeclaration.setBodyText("");
|
|
32
|
+
});
|
|
33
|
+
return file.print();
|
|
34
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join, posix } from "node:path";
|
|
3
|
+
import { globSync } from "glob";
|
|
4
|
+
import { normalizePath } from "../../utils/index.js";
|
|
5
|
+
export function patchBuildId(code, config) {
|
|
6
|
+
// The next-server code gets the buildId from the filesystem, resulting in a `[unenv] fs.readFileSync is not implemented yet!` error
|
|
7
|
+
// so we add an early return to the `getBuildId` function so that the `readyFileSync` is never encountered
|
|
8
|
+
// (source: https://github.com/vercel/next.js/blob/15aeb92efb34c09a36/packages/next/src/server/next-server.ts#L438-L451)
|
|
9
|
+
// Note: we could/should probably just patch readFileSync here or something!
|
|
10
|
+
return code.replace("getBuildId() {", `getBuildId() {
|
|
11
|
+
return ${JSON.stringify(readFileSync(join(config.paths.output.standaloneAppDotNext, "BUILD_ID"), "utf-8"))};
|
|
12
|
+
`);
|
|
13
|
+
}
|
|
14
|
+
export function patchLoadManifest(code, config) {
|
|
15
|
+
// Same as patchBuildId, the next-server code loads the manifests with `readFileSync` and we want to avoid that
|
|
16
|
+
// (source: https://github.com/vercel/next.js/blob/15aeb92e/packages/next/src/server/load-manifest.ts#L34-L56)
|
|
17
|
+
// Note: we could/should probably just patch readFileSync here or something!
|
|
18
|
+
const manifestJsons = globSync(normalizePath(join(config.paths.output.standaloneAppDotNext, "**", "*-manifest.json"))).map((file) => normalizePath(file).replace(normalizePath(config.paths.output.standaloneApp) + posix.sep, ""));
|
|
19
|
+
return code.replace(/function loadManifest\((.+?), .+?\) {/, `$&
|
|
20
|
+
${manifestJsons
|
|
21
|
+
.map((manifestJson) => `
|
|
22
|
+
if ($1.endsWith("${manifestJson}")) {
|
|
23
|
+
return ${readFileSync(join(config.paths.output.standaloneApp, manifestJson), "utf-8")};
|
|
24
|
+
}
|
|
25
|
+
`)
|
|
26
|
+
.join("\n")}
|
|
27
|
+
throw new Error("Unknown loadManifest: " + $1);
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function patchWranglerDeps(config) {
|
|
4
|
+
console.log("# patchWranglerDeps");
|
|
5
|
+
const distPath = getDistPath(config);
|
|
6
|
+
// Patch .next/standalone/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js
|
|
7
|
+
//
|
|
8
|
+
// Remove the need for an alias in wrangler.toml:
|
|
9
|
+
//
|
|
10
|
+
// [alias]
|
|
11
|
+
// # critters is `require`d from `pages.runtime.prod.js` when running wrangler dev, so we need to stub it out
|
|
12
|
+
// "critters" = "./.next/standalone/node_modules/cf/templates/shims/empty.ts"
|
|
13
|
+
const pagesRuntimeFile = join(distPath, "compiled", "next-server", "pages.runtime.prod.js");
|
|
14
|
+
const patchedPagesRuntime = readFileSync(pagesRuntimeFile, "utf-8").replace(`e.exports=require("critters")`, `e.exports={}`);
|
|
15
|
+
writeFileSync(pagesRuntimeFile, patchedPagesRuntime);
|
|
16
|
+
// Patch .next/standalone/node_modules/next/dist/server/lib/trace/tracer.js
|
|
17
|
+
//
|
|
18
|
+
// Remove the need for an alias in wrangler.toml:
|
|
19
|
+
//
|
|
20
|
+
// [alias]
|
|
21
|
+
// # @opentelemetry/api is `require`d when running wrangler dev, so we need to stub it out
|
|
22
|
+
// # IMPORTANT: we shim @opentelemetry/api to the throwing shim so that it will throw right away, this is so that we throw inside the
|
|
23
|
+
// # try block here: https://github.com/vercel/next.js/blob/9e8266a7/packages/next/src/server/lib/trace/tracer.ts#L27-L31
|
|
24
|
+
// # causing the code to require the 'next/dist/compiled/@opentelemetry/api' module instead (which properly works)
|
|
25
|
+
// #"@opentelemetry/api" = "./.next/standalone/node_modules/cf/templates/shims/throw.ts"
|
|
26
|
+
const tracerFile = join(distPath, "server", "lib", "trace", "tracer.js");
|
|
27
|
+
const patchedTracer = readFileSync(tracerFile, "utf-8").replaceAll(/\w+\s*=\s*require\([^/]*opentelemetry.*\)/g, `throw new Error("@opentelemetry/api")`);
|
|
28
|
+
writeFileSync(tracerFile, patchedTracer);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Next.js saves the node_modules/next/dist directory in either the standaloneApp path or in the
|
|
32
|
+
* standaloneRoot path, this depends on where the next dependency is actually saved (
|
|
33
|
+
* https://github.com/vercel/next.js/blob/39e06c75/packages/next/src/build/webpack-config.ts#L103-L104
|
|
34
|
+
* ) and can depend on the package manager used, if it is using workspaces, etc...
|
|
35
|
+
*
|
|
36
|
+
* This function checks the two potential paths for the dist directory and returns the first that it finds,
|
|
37
|
+
* it throws an error if it can't find either
|
|
38
|
+
*
|
|
39
|
+
* @param config
|
|
40
|
+
* @returns the node_modules/next/dist directory path
|
|
41
|
+
*/
|
|
42
|
+
function getDistPath(config) {
|
|
43
|
+
for (const root of [config.paths.output.standaloneApp, config.paths.output.standaloneRoot]) {
|
|
44
|
+
try {
|
|
45
|
+
const distPath = join(root, "node_modules", "next", "dist");
|
|
46
|
+
if (statSync(distPath).isDirectory())
|
|
47
|
+
return distPath;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* empty */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new Error("Unexpected error: unable to detect the node_modules/next/dist directory");
|
|
54
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the environment variables defined in various .env files for a project.
|
|
4
|
+
*
|
|
5
|
+
* The `NEXTJS_ENV` environment variable in `.dev.vars` determines the mode.
|
|
6
|
+
*
|
|
7
|
+
* Merged variables respect the following priority order.
|
|
8
|
+
* 1. `.env.{mode}.local`
|
|
9
|
+
* 2. `.env.local`
|
|
10
|
+
* 3. `.env.{mode}`
|
|
11
|
+
* 4. `.env`
|
|
12
|
+
*
|
|
13
|
+
* https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order
|
|
14
|
+
*
|
|
15
|
+
* In a monorepo, the env files in an app's directory will take precedence over
|
|
16
|
+
* the env files at the root of the monorepo.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractProjectEnvVars(mode: string, { monorepoRoot, appPath }: BuildOptions): Record<string, string>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { parse } from "@dotenvx/dotenvx";
|
|
4
|
+
function readEnvFile(filePath) {
|
|
5
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
6
|
+
return parse(fs.readFileSync(filePath).toString());
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extracts the environment variables defined in various .env files for a project.
|
|
11
|
+
*
|
|
12
|
+
* The `NEXTJS_ENV` environment variable in `.dev.vars` determines the mode.
|
|
13
|
+
*
|
|
14
|
+
* Merged variables respect the following priority order.
|
|
15
|
+
* 1. `.env.{mode}.local`
|
|
16
|
+
* 2. `.env.local`
|
|
17
|
+
* 3. `.env.{mode}`
|
|
18
|
+
* 4. `.env`
|
|
19
|
+
*
|
|
20
|
+
* https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order
|
|
21
|
+
*
|
|
22
|
+
* In a monorepo, the env files in an app's directory will take precedence over
|
|
23
|
+
* the env files at the root of the monorepo.
|
|
24
|
+
*/
|
|
25
|
+
export function extractProjectEnvVars(mode, { monorepoRoot, appPath }) {
|
|
26
|
+
return [".env", `.env.${mode}`, ".env.local", `.env.${mode}.local`]
|
|
27
|
+
.flatMap((fileName) => [
|
|
28
|
+
...(monorepoRoot !== appPath ? [readEnvFile(path.join(monorepoRoot, fileName))] : []),
|
|
29
|
+
readEnvFile(path.join(appPath, fileName)),
|
|
30
|
+
])
|
|
31
|
+
.reduce((acc, overrides) => ({ ...acc, ...overrides }), {});
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|