@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.
Files changed (100) hide show
  1. package/README.md +48 -38
  2. package/dist/api/{get-cloudflare-context.d.mts → get-cloudflare-context.d.ts} +4 -4
  3. package/dist/api/get-cloudflare-context.js +39 -0
  4. package/dist/api/index.d.ts +1 -0
  5. package/dist/api/index.js +1 -0
  6. package/dist/api/kvCache.d.ts +27 -0
  7. package/dist/api/kvCache.js +121 -0
  8. package/dist/cli/args.d.ts +5 -0
  9. package/dist/cli/args.js +48 -0
  10. package/dist/cli/build/bundle-server.d.ts +6 -0
  11. package/dist/cli/build/bundle-server.js +188 -0
  12. package/dist/cli/build/index.d.ts +9 -0
  13. package/dist/cli/build/index.js +123 -0
  14. package/dist/cli/build/open-next/compile-env-files.d.ts +5 -0
  15. package/dist/cli/build/open-next/compile-env-files.js +9 -0
  16. package/dist/cli/build/open-next/copyCacheAssets.d.ts +2 -0
  17. package/dist/cli/build/open-next/copyCacheAssets.js +10 -0
  18. package/dist/cli/build/open-next/createServerBundle.d.ts +2 -0
  19. package/dist/cli/build/open-next/createServerBundle.js +216 -0
  20. package/dist/cli/build/patches/index.d.ts +2 -0
  21. package/dist/cli/build/patches/index.js +2 -0
  22. package/dist/cli/build/patches/investigated/copy-package-cli-files.d.ts +6 -0
  23. package/dist/cli/build/patches/investigated/copy-package-cli-files.js +12 -0
  24. package/dist/cli/build/patches/investigated/index.d.ts +4 -0
  25. package/dist/cli/build/patches/investigated/index.js +4 -0
  26. package/dist/cli/build/patches/investigated/patch-cache.d.ts +13 -0
  27. package/dist/cli/build/patches/investigated/patch-cache.js +22 -0
  28. package/dist/cli/build/patches/investigated/patch-require.d.ts +7 -0
  29. package/dist/cli/build/patches/investigated/patch-require.js +9 -0
  30. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.d.ts +13 -0
  31. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.js +82 -0
  32. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.d.ts +1 -0
  33. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.js +20 -0
  34. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.d.ts +19 -0
  35. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.js +76 -0
  36. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.d.ts +1 -0
  37. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.js +23 -0
  38. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.d.ts +14 -0
  39. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.js +22 -0
  40. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.d.ts +1 -0
  41. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.js +15 -0
  42. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.d.ts +8 -0
  43. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.js +22 -0
  44. package/dist/cli/build/patches/to-investigate/index.d.ts +8 -0
  45. package/dist/cli/build/patches/to-investigate/index.js +8 -0
  46. package/dist/cli/build/patches/to-investigate/inline-eval-manifest.d.ts +9 -0
  47. package/dist/cli/build/patches/to-investigate/inline-eval-manifest.js +32 -0
  48. package/dist/cli/build/patches/to-investigate/inline-middleware-manifest-require.d.ts +6 -0
  49. package/dist/cli/build/patches/to-investigate/inline-middleware-manifest-require.js +13 -0
  50. package/dist/cli/build/patches/to-investigate/inline-next-require.d.ts +6 -0
  51. package/dist/cli/build/patches/to-investigate/inline-next-require.js +36 -0
  52. package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.d.ts +7 -0
  53. package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.js +9 -0
  54. package/dist/cli/build/patches/to-investigate/patch-find-dir.d.ts +8 -0
  55. package/dist/cli/build/patches/to-investigate/patch-find-dir.js +21 -0
  56. package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.d.ts +14 -0
  57. package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.js +34 -0
  58. package/dist/cli/build/patches/to-investigate/patch-read-file.d.ts +3 -0
  59. package/dist/cli/build/patches/to-investigate/patch-read-file.js +29 -0
  60. package/dist/cli/build/patches/to-investigate/wrangler-deps.d.ts +2 -0
  61. package/dist/cli/build/patches/to-investigate/wrangler-deps.js +54 -0
  62. package/dist/cli/build/utils/extract-project-env-vars.d.ts +18 -0
  63. package/dist/cli/build/utils/extract-project-env-vars.js +32 -0
  64. package/dist/cli/build/utils/extract-project-env-vars.spec.d.ts +1 -0
  65. package/dist/cli/build/utils/extract-project-env-vars.spec.js +57 -0
  66. package/dist/cli/build/utils/index.d.ts +3 -0
  67. package/dist/cli/build/utils/index.js +3 -0
  68. package/dist/cli/build/utils/normalize-path.d.ts +1 -0
  69. package/dist/cli/build/utils/normalize-path.js +4 -0
  70. package/dist/cli/build/utils/read-paths-recursively.d.ts +7 -0
  71. package/dist/cli/build/utils/read-paths-recursively.js +20 -0
  72. package/dist/cli/build/utils/ts-parse-file.d.ts +8 -0
  73. package/dist/cli/build/utils/ts-parse-file.js +12 -0
  74. package/dist/cli/config.d.ts +41 -0
  75. package/dist/cli/config.js +92 -0
  76. package/dist/cli/index.d.ts +2 -0
  77. package/dist/cli/index.js +12 -0
  78. package/dist/cli/templates/shims/empty.d.ts +2 -0
  79. package/dist/cli/templates/shims/env.d.ts +1 -0
  80. package/dist/cli/templates/shims/env.js +1 -0
  81. package/dist/cli/templates/shims/node-fs.d.ts +17 -0
  82. package/dist/cli/templates/shims/node-fs.js +51 -0
  83. package/dist/cli/templates/shims/throw.d.ts +0 -0
  84. package/dist/cli/templates/shims/{throw.ts → throw.js} +1 -0
  85. package/dist/cli/templates/worker.d.ts +5 -0
  86. package/dist/cli/templates/worker.js +67 -0
  87. package/package.json +29 -12
  88. package/dist/api/chunk-VTBEIZPQ.mjs +0 -32
  89. package/dist/api/get-cloudflare-context.mjs +0 -6
  90. package/dist/api/index.d.mts +0 -1
  91. package/dist/api/index.mjs +0 -6
  92. package/dist/cli/constants/incremental-cache.ts +0 -8
  93. package/dist/cli/index.mjs +0 -7422
  94. package/dist/cli/templates/cache-handler/index.ts +0 -1
  95. package/dist/cli/templates/cache-handler/open-next-cache-handler.ts +0 -148
  96. package/dist/cli/templates/cache-handler/utils.ts +0 -41
  97. package/dist/cli/templates/shims/env.ts +0 -1
  98. package/dist/cli/templates/shims/node-fs.ts +0 -69
  99. package/dist/cli/templates/worker.ts +0 -156
  100. /package/dist/cli/templates/shims/{empty.ts → empty.js} +0 -0
@@ -0,0 +1,123 @@
1
+ import { cpSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { buildNextjsApp, setStandaloneBuildMode } from "@opennextjs/aws/build/buildNextApp.js";
5
+ import { compileCache } from "@opennextjs/aws/build/compileCache.js";
6
+ import { compileOpenNextConfig } from "@opennextjs/aws/build/compileConfig.js";
7
+ import { createCacheAssets, createStaticAssets } from "@opennextjs/aws/build/createAssets.js";
8
+ import { createMiddleware } from "@opennextjs/aws/build/createMiddleware.js";
9
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
10
+ import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
11
+ import logger from "@opennextjs/aws/logger.js";
12
+ import { containsDotNextDir, getConfig } from "../config.js";
13
+ import { bundleServer } from "./bundle-server.js";
14
+ import { compileEnvFiles } from "./open-next/compile-env-files.js";
15
+ import { copyCacheAssets } from "./open-next/copyCacheAssets.js";
16
+ import { createServerBundle } from "./open-next/createServerBundle.js";
17
+ /**
18
+ * Builds the application in a format that can be passed to workerd
19
+ *
20
+ * It saves the output in a `.worker-next` directory
21
+ *
22
+ * @param projectOpts The options for the project
23
+ */
24
+ export async function build(projectOpts) {
25
+ printHeader("Cloudflare build");
26
+ showWarningOnWindows();
27
+ const baseDir = projectOpts.sourceDir;
28
+ const require = createRequire(import.meta.url);
29
+ const openNextDistDir = dirname(require.resolve("@opennextjs/aws/index.js"));
30
+ const { config, buildDir } = await compileOpenNextConfig(baseDir);
31
+ ensureCloudflareConfig(config);
32
+ // Initialize options
33
+ const options = buildHelper.normalizeOptions(config, openNextDistDir, buildDir);
34
+ logger.setLevel(options.debug ? "debug" : "info");
35
+ // Do not minify the code so that we can apply string replacement patch.
36
+ // Note that wrangler will still minify the bundle.
37
+ options.minify = false;
38
+ // Pre-build validation
39
+ buildHelper.checkRunningInsideNextjsApp(options);
40
+ logger.info(`App directory: ${options.appPath}`);
41
+ buildHelper.printNextjsVersion(options);
42
+ buildHelper.printOpenNextVersion(options);
43
+ if (projectOpts.skipNextBuild) {
44
+ logger.warn("Skipping Next.js build");
45
+ }
46
+ else {
47
+ // Build the next app
48
+ printHeader("Building Next.js app");
49
+ setStandaloneBuildMode(options);
50
+ buildNextjsApp(options);
51
+ }
52
+ if (!containsDotNextDir(projectOpts.sourceDir)) {
53
+ throw new Error(`.next folder not found in ${projectOpts.sourceDir}`);
54
+ }
55
+ // Generate deployable bundle
56
+ printHeader("Generating bundle");
57
+ buildHelper.initOutputDir(options);
58
+ // Compile cache.ts
59
+ compileCache(options);
60
+ // Compile .env files
61
+ compileEnvFiles(options);
62
+ // Compile middleware
63
+ await createMiddleware(options, { forceOnlyBuildOnce: true });
64
+ createStaticAssets(options);
65
+ if (config.dangerous?.disableIncrementalCache !== true) {
66
+ createCacheAssets(options);
67
+ copyCacheAssets(options);
68
+ }
69
+ await createServerBundle(options);
70
+ // TODO: drop this copy.
71
+ // Copy the .next directory to the output directory so it can be mutated.
72
+ cpSync(join(projectOpts.sourceDir, ".next"), join(projectOpts.outputDir, ".next"), { recursive: true });
73
+ const projConfig = getConfig(projectOpts);
74
+ // TODO: rely on options only.
75
+ await bundleServer(projConfig, options);
76
+ logger.info("OpenNext build complete.");
77
+ }
78
+ /**
79
+ * Ensures open next is configured for cloudflare.
80
+ *
81
+ * @param config OpenNext configuration.
82
+ */
83
+ function ensureCloudflareConfig(config) {
84
+ const requirements = {
85
+ dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
86
+ dftUseEdgeConverter: config.default?.override?.converter === "edge",
87
+ dftMaybeUseCache: config.default?.override?.incrementalCache === "dummy" ||
88
+ typeof config.default?.override?.incrementalCache === "function",
89
+ dftUseDummyTagCacheAndQueue: config.default?.override?.tagCache === "dummy" && config.default?.override?.queue === "dummy",
90
+ disableCacheInterception: config.dangerous?.enableCacheInterception !== true,
91
+ mwIsMiddlewareExternal: config.middleware?.external == true,
92
+ mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
93
+ mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
94
+ mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
95
+ };
96
+ if (Object.values(requirements).some((satisfied) => !satisfied)) {
97
+ throw new Error(`open-next.config.ts should contain:
98
+ {
99
+ default: {
100
+ override: {
101
+ wrapper: "cloudflare-node",
102
+ converter: "edge",
103
+ incrementalCache: "dummy" | function,
104
+ tagCache: "dummy",
105
+ queue: "dummy",
106
+ },
107
+ },
108
+
109
+ middleware: {
110
+ external: true,
111
+ override: {
112
+ wrapper: "cloudflare-edge",
113
+ converter: "edge",
114
+ proxyExternalRequest: "fetch",
115
+ },
116
+ },
117
+
118
+ "dangerous": {
119
+ "enableCacheInterception": false
120
+ }
121
+ }`);
122
+ }
123
+ }
@@ -0,0 +1,5 @@
1
+ import { BuildOptions } from "@opennextjs/aws/build/helper.js";
2
+ /**
3
+ * Compiles the values extracted from the project's env files to the output directory for use in the worker.
4
+ */
5
+ export declare function compileEnvFiles(options: BuildOptions): void;
@@ -0,0 +1,9 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { extractProjectEnvVars } from "../utils/index.js";
4
+ /**
5
+ * Compiles the values extracted from the project's env files to the output directory for use in the worker.
6
+ */
7
+ export function compileEnvFiles(options) {
8
+ ["production", "development", "test"].forEach((mode) => fs.appendFileSync(path.join(options.outputDir, `.env.mjs`), `export const ${mode} = ${JSON.stringify(extractProjectEnvVars(mode, options))};\n`));
9
+ }
@@ -0,0 +1,2 @@
1
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
2
+ export declare function copyCacheAssets(options: buildHelper.BuildOptions): void;
@@ -0,0 +1,10 @@
1
+ import { cpSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { CACHE_ASSET_DIR } from "../../../api/kvCache.js";
4
+ export function copyCacheAssets(options) {
5
+ const { outputDir } = options;
6
+ const srcPath = join(outputDir, "cache");
7
+ const dstPath = join(outputDir, "assets", CACHE_ASSET_DIR);
8
+ mkdirSync(dstPath, { recursive: true });
9
+ cpSync(srcPath, dstPath, { recursive: true });
10
+ }
@@ -0,0 +1,2 @@
1
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
2
+ export declare function createServerBundle(options: buildHelper.BuildOptions): Promise<void>;
@@ -0,0 +1,216 @@
1
+ // Copy-Edit of @opennextjs/aws packages/open-next/src/build/createServerBundle.ts
2
+ // Adapted for cloudflare workers
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { bundleNextServer } from "@opennextjs/aws/build/bundleNextServer.js";
6
+ import { compileCache } from "@opennextjs/aws/build/compileCache.js";
7
+ import { copyTracedFiles } from "@opennextjs/aws/build/copyTracedFiles.js";
8
+ import { generateEdgeBundle } from "@opennextjs/aws/build/edge/createEdgeBundle.js";
9
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
10
+ import { installDependencies } from "@opennextjs/aws/build/installDeps.js";
11
+ import logger from "@opennextjs/aws/logger.js";
12
+ import { minifyAll } from "@opennextjs/aws/minimize-js.js";
13
+ import { openNextEdgePlugins } from "@opennextjs/aws/plugins/edge.js";
14
+ import { openNextReplacementPlugin } from "@opennextjs/aws/plugins/replacement.js";
15
+ import { openNextResolvePlugin } from "@opennextjs/aws/plugins/resolve.js";
16
+ export async function createServerBundle(options) {
17
+ const { config } = options;
18
+ const foundRoutes = new Set();
19
+ // Get all functions to build
20
+ const defaultFn = config.default;
21
+ const functions = Object.entries(config.functions ?? {});
22
+ // Recompile cache.ts as ESM if any function is using Deno runtime
23
+ if (defaultFn.runtime === "deno" || functions.some(([, fn]) => fn.runtime === "deno")) {
24
+ compileCache(options, "esm");
25
+ }
26
+ const promises = functions.map(async ([name, fnOptions]) => {
27
+ const routes = fnOptions.routes;
28
+ routes.forEach((route) => foundRoutes.add(route));
29
+ if (fnOptions.runtime === "edge") {
30
+ await generateEdgeBundle(name, options, fnOptions);
31
+ }
32
+ else {
33
+ await generateBundle(name, options, fnOptions);
34
+ }
35
+ });
36
+ //TODO: throw an error if not all edge runtime routes has been bundled in a separate function
37
+ // We build every other function than default before so we know which route there is left
38
+ await Promise.all(promises);
39
+ const remainingRoutes = new Set();
40
+ const { appBuildOutputPath, monorepoRoot } = options;
41
+ const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
42
+ // Find remaining routes
43
+ const serverPath = path.join(appBuildOutputPath, ".next", "standalone", packagePath, ".next", "server");
44
+ // Find app dir routes
45
+ if (fs.existsSync(path.join(serverPath, "app"))) {
46
+ const appPath = path.join(serverPath, "app");
47
+ buildHelper.traverseFiles(appPath, ({ relativePath }) => relativePath.endsWith("page.js") || relativePath.endsWith("route.js"), ({ relativePath }) => {
48
+ const route = `app/${relativePath.replace(/\.js$/, "")}`;
49
+ if (!foundRoutes.has(route)) {
50
+ remainingRoutes.add(route);
51
+ }
52
+ });
53
+ }
54
+ // Find pages dir routes
55
+ if (fs.existsSync(path.join(serverPath, "pages"))) {
56
+ const pagePath = path.join(serverPath, "pages");
57
+ buildHelper.traverseFiles(pagePath, ({ relativePath }) => relativePath.endsWith(".js"), ({ relativePath }) => {
58
+ const route = `pages/${relativePath.replace(/\.js$/, "")}`;
59
+ if (!foundRoutes.has(route)) {
60
+ remainingRoutes.add(route);
61
+ }
62
+ });
63
+ }
64
+ // Generate default function
65
+ await generateBundle("default", options, {
66
+ ...defaultFn,
67
+ // @ts-expect-error - Those string are RouteTemplate
68
+ routes: Array.from(remainingRoutes),
69
+ patterns: ["*"],
70
+ });
71
+ }
72
+ async function generateBundle(name, options, fnOptions) {
73
+ const { appPath, appBuildOutputPath, config, outputDir, monorepoRoot } = options;
74
+ logger.info(`Building server function: ${name}...`);
75
+ // Create output folder
76
+ const outputPath = path.join(outputDir, "server-functions", name);
77
+ // Resolve path to the Next.js app if inside the monorepo
78
+ // note: if user's app is inside a monorepo, standalone mode places
79
+ // `node_modules` inside `.next/standalone`, and others inside
80
+ // `.next/standalone/package/path` (ie. `.next`, `server.js`).
81
+ // We need to output the handler file inside the package path.
82
+ const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
83
+ fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true });
84
+ const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs";
85
+ fs.copyFileSync(path.join(options.buildDir, `cache.${ext}`), path.join(outputPath, packagePath, "cache.cjs"));
86
+ if (fnOptions.runtime === "deno") {
87
+ addDenoJson(outputPath, packagePath);
88
+ }
89
+ // Bundle next server if necessary
90
+ const isBundled = fnOptions.experimentalBundledNextServer ?? false;
91
+ if (isBundled) {
92
+ await bundleNextServer(path.join(outputPath, packagePath), appPath, {
93
+ minify: options.minify,
94
+ });
95
+ }
96
+ // Copy middleware
97
+ if (!config.middleware?.external) {
98
+ fs.copyFileSync(path.join(options.buildDir, "middleware.mjs"), path.join(outputPath, packagePath, "middleware.mjs"));
99
+ }
100
+ // Copy open-next.config.mjs
101
+ buildHelper.copyOpenNextConfig(options.buildDir, path.join(outputPath, packagePath), true);
102
+ // Copy env files
103
+ buildHelper.copyEnvFile(appBuildOutputPath, packagePath, outputPath);
104
+ // Copy all necessary traced files
105
+ await copyTracedFiles(appBuildOutputPath, packagePath, outputPath, fnOptions.routes ?? ["app/page.tsx"], isBundled);
106
+ // Build Lambda code
107
+ // note: bundle in OpenNext package b/c the adapter relies on the
108
+ // "serverless-http" package which is not a dependency in user's
109
+ // Next.js app.
110
+ const disableNextPrebundledReact = buildHelper.compareSemver(options.nextVersion, "13.5.1") >= 0 ||
111
+ buildHelper.compareSemver(options.nextVersion, "13.4.1") <= 0;
112
+ const overrides = fnOptions.override ?? {};
113
+ const isBefore13413 = buildHelper.compareSemver(options.nextVersion, "13.4.13") <= 0;
114
+ const isAfter141 = buildHelper.compareSemver(options.nextVersion, "14.0.4") >= 0;
115
+ const disableRouting = isBefore13413 || config.middleware?.external;
116
+ const plugins = [
117
+ openNextReplacementPlugin({
118
+ name: `requestHandlerOverride ${name}`,
119
+ target: /core(\/|\\)requestHandler\.js/g,
120
+ deletes: [
121
+ ...(disableNextPrebundledReact ? ["applyNextjsPrebundledReact"] : []),
122
+ ...(disableRouting ? ["withRouting"] : []),
123
+ ],
124
+ }),
125
+ openNextReplacementPlugin({
126
+ name: `utilOverride ${name}`,
127
+ target: /core(\/|\\)util\.js/g,
128
+ deletes: [
129
+ ...(disableNextPrebundledReact ? ["requireHooks"] : []),
130
+ ...(disableRouting ? ["trustHostHeader"] : []),
131
+ ...(!isBefore13413 ? ["requestHandlerHost"] : []),
132
+ ...(isAfter141 ? ["experimentalIncrementalCacheHandler"] : ["stableIncrementalCache"]),
133
+ ],
134
+ }),
135
+ openNextResolvePlugin({
136
+ fnName: name,
137
+ overrides,
138
+ }),
139
+ openNextEdgePlugins({
140
+ nextDir: path.join(options.appBuildOutputPath, ".next"),
141
+ edgeFunctionHandlerPath: path.join(options.openNextDistDir, "core", "edgeFunctionHandler.js"),
142
+ isInCloudfare: true,
143
+ }),
144
+ ];
145
+ const outfileExt = fnOptions.runtime === "deno" ? "ts" : "mjs";
146
+ await buildHelper.esbuildAsync({
147
+ entryPoints: [path.join(options.openNextDistDir, "adapters", "server-adapter.js")],
148
+ outfile: path.join(outputPath, packagePath, `index.${outfileExt}`),
149
+ banner: {
150
+ js: [
151
+ `globalThis.monorepoPackagePath = "${packagePath}";`,
152
+ name === "default" ? "" : `globalThis.fnName = "${name}";`,
153
+ ].join(""),
154
+ },
155
+ plugins,
156
+ alias: {
157
+ ...(isBundled
158
+ ? {
159
+ "next/dist/server/next-server.js": "./next-server.runtime.prod.js",
160
+ }
161
+ : {}),
162
+ },
163
+ }, options);
164
+ const isMonorepo = monorepoRoot !== appPath;
165
+ if (isMonorepo) {
166
+ addMonorepoEntrypoint(outputPath, packagePath);
167
+ }
168
+ installDependencies(outputPath, fnOptions.install);
169
+ if (fnOptions.minify) {
170
+ await minifyServerBundle(outputPath);
171
+ }
172
+ const shouldGenerateDocker = shouldGenerateDockerfile(fnOptions);
173
+ if (shouldGenerateDocker) {
174
+ fs.writeFileSync(path.join(outputPath, "Dockerfile"), typeof shouldGenerateDocker === "string"
175
+ ? shouldGenerateDocker
176
+ : `
177
+ FROM node:18-alpine
178
+ WORKDIR /app
179
+ COPY . /app
180
+ EXPOSE 3000
181
+ CMD ["node", "index.mjs"]
182
+ `);
183
+ }
184
+ }
185
+ function shouldGenerateDockerfile(options) {
186
+ return options.override?.generateDockerfile ?? false;
187
+ }
188
+ // Add deno.json file to enable "bring your own node_modules" mode.
189
+ // TODO: this won't be necessary in Deno 2. See https://github.com/denoland/deno/issues/23151
190
+ function addDenoJson(outputPath, packagePath) {
191
+ const config = {
192
+ // Enable "bring your own node_modules" mode
193
+ // and allow `__proto__`
194
+ unstable: ["byonm", "fs", "unsafe-proto"],
195
+ };
196
+ fs.writeFileSync(path.join(outputPath, packagePath, "deno.json"), JSON.stringify(config, null, 2));
197
+ }
198
+ //TODO: check if this PR is still necessary https://github.com/opennextjs/opennextjs-aws/pull/341
199
+ function addMonorepoEntrypoint(outputPath, packagePath) {
200
+ // Note: in the monorepo case, the handler file is output to
201
+ // `.next/standalone/package/path/index.mjs`, but we want
202
+ // the Lambda function to be able to find the handler at
203
+ // the root of the bundle. We will create a dummy `index.mjs`
204
+ // that re-exports the real handler.
205
+ // TOOD: use helper
206
+ // Always use posix path for import path
207
+ const packagePosixPath = packagePath.split(path.sep).join(path.posix.sep);
208
+ fs.writeFileSync(path.join(outputPath, "index.mjs"), `export * from "./${packagePosixPath}/index.mjs";`);
209
+ }
210
+ async function minifyServerBundle(outputDir) {
211
+ logger.info("Minimizing server function...");
212
+ await minifyAll(outputDir, {
213
+ compress_json: true,
214
+ mangle: true,
215
+ });
216
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./investigated/index.js";
2
+ export * from "./to-investigate/index.js";
@@ -0,0 +1,2 @@
1
+ export * from "./investigated/index.js";
2
+ export * from "./to-investigate/index.js";
@@ -0,0 +1,6 @@
1
+ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
2
+ import { Config } from "../../../config.js";
3
+ /**
4
+ * Copies the template files present in the cloudflare adapter package into the standalone node_modules folder
5
+ */
6
+ export declare function copyPackageCliFiles(packageDistDir: string, config: Config, openNextConfig: BuildOptions): void;
@@ -0,0 +1,12 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ /**
4
+ * Copies the template files present in the cloudflare adapter package into the standalone node_modules folder
5
+ */
6
+ export function copyPackageCliFiles(packageDistDir, config, openNextConfig) {
7
+ console.log("# copyPackageTemplateFiles");
8
+ const sourceDir = path.join(packageDistDir, "cli");
9
+ const destinationDir = path.join(config.paths.internal.package, "cli");
10
+ fs.cpSync(sourceDir, destinationDir, { recursive: true });
11
+ fs.copyFileSync(path.join(packageDistDir, "cli", "templates", "worker.js"), path.join(openNextConfig.outputDir, "worker.js"));
12
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./copy-package-cli-files.js";
2
+ export * from "./patch-cache.js";
3
+ export * from "./patch-require.js";
4
+ export * from "./update-webpack-chunks-file/index.js";
@@ -0,0 +1,4 @@
1
+ export * from "./copy-package-cli-files.js";
2
+ export * from "./patch-cache.js";
3
+ export * from "./patch-require.js";
4
+ export * from "./update-webpack-chunks-file/index.js";
@@ -0,0 +1,13 @@
1
+ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
2
+ /**
3
+ * Sets up the OpenNext cache handler in a Next.js build.
4
+ *
5
+ * The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
6
+ * Next.js would then do a dynamic require on a transformed version of the path to retrieve the
7
+ * cache handler and create a new instance of it.
8
+ *
9
+ * This is problematic in workerd due to the dynamic import of the file that is not known from
10
+ * build-time. Therefore, we have to manually override the default way that the cache handler is
11
+ * instantiated with a dynamic require that uses a string literal for the path.
12
+ */
13
+ export declare function patchCache(code: string, openNextOptions: BuildOptions): Promise<string>;
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ /**
3
+ * Sets up the OpenNext cache handler in a Next.js build.
4
+ *
5
+ * The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
6
+ * Next.js would then do a dynamic require on a transformed version of the path to retrieve the
7
+ * cache handler and create a new instance of it.
8
+ *
9
+ * This is problematic in workerd due to the dynamic import of the file that is not known from
10
+ * build-time. Therefore, we have to manually override the default way that the cache handler is
11
+ * instantiated with a dynamic require that uses a string literal for the path.
12
+ */
13
+ export async function patchCache(code, openNextOptions) {
14
+ const { appBuildOutputPath, outputDir, monorepoRoot } = openNextOptions;
15
+ // TODO: switch to cache.mjs
16
+ const outputPath = path.join(outputDir, "server-functions", "default");
17
+ const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
18
+ const cacheFile = path.join(outputPath, packagePath, "cache.cjs");
19
+ return code.replace("const { cacheHandler } = this.nextConfig;", `const cacheHandler = null;
20
+ CacheHandler = require('${cacheFile}').default;
21
+ `);
22
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ESBuild does not support CJS format
3
+ * See https://github.com/evanw/esbuild/issues/1921 and linked issues
4
+ * Some of the solutions are based on `module.createRequire()` not implemented in workerd.
5
+ * James on Aug 29: `module.createRequire()` is planned.
6
+ */
7
+ export declare function patchRequire(code: string): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * ESBuild does not support CJS format
3
+ * See https://github.com/evanw/esbuild/issues/1921 and linked issues
4
+ * Some of the solutions are based on `module.createRequire()` not implemented in workerd.
5
+ * James on Aug 29: `module.createRequire()` is planned.
6
+ */
7
+ export function patchRequire(code) {
8
+ return code.replace(/__require\d?\(/g, "require(").replace(/__require\d?\./g, "require.");
9
+ }
@@ -0,0 +1,13 @@
1
+ import * as ts from "ts-morph";
2
+ /**
3
+ * Gets the names of the variables that in the unminified webpack runtime file are called `installedChunks` and `installChunk`.
4
+ *
5
+ * Variables example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L282
6
+ *
7
+ * @param sourceFile the webpack runtime file parsed with ts-morph
8
+ * @returns an object containing the two variable names
9
+ */
10
+ export declare function getChunkInstallationIdentifiers(sourceFile: ts.SourceFile): Promise<{
11
+ installedChunks: string;
12
+ installChunk: string;
13
+ }>;
@@ -0,0 +1,82 @@
1
+ import * as ts from "ts-morph";
2
+ /**
3
+ * Gets the names of the variables that in the unminified webpack runtime file are called `installedChunks` and `installChunk`.
4
+ *
5
+ * Variables example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L282
6
+ *
7
+ * @param sourceFile the webpack runtime file parsed with ts-morph
8
+ * @returns an object containing the two variable names
9
+ */
10
+ export async function getChunkInstallationIdentifiers(sourceFile) {
11
+ const installChunkDeclaration = getInstallChunkDeclaration(sourceFile);
12
+ const installedChunksDeclaration = getInstalledChunksDeclaration(sourceFile, installChunkDeclaration);
13
+ return {
14
+ installChunk: installChunkDeclaration.getName(),
15
+ installedChunks: installedChunksDeclaration.getName(),
16
+ };
17
+ }
18
+ /**
19
+ * Gets the declaration for what in the unminified webpack runtime file is called `installChunk`(which is a function that registers the various chunks.
20
+ *
21
+ * `installChunk` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L263-L282
22
+ *
23
+ * @param sourceFile the webpack runtime file parsed with ts-morph
24
+ * @returns the `installChunk` declaration
25
+ */
26
+ function getInstallChunkDeclaration(sourceFile) {
27
+ const installChunkDeclaration = sourceFile
28
+ .getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration)
29
+ .find((declaration) => {
30
+ const arrowFunction = declaration.getInitializerIfKind(ts.SyntaxKind.ArrowFunction);
31
+ // we're looking for an arrow function
32
+ if (!arrowFunction)
33
+ return false;
34
+ const functionParameters = arrowFunction.getParameters();
35
+ // the arrow function we're looking for has a single parameter (the chunkId)
36
+ if (functionParameters.length !== 1)
37
+ return false;
38
+ const arrowFunctionBodyBlock = arrowFunction.getFirstChildByKind(ts.SyntaxKind.Block);
39
+ // the arrow function we're looking for has a block body
40
+ if (!arrowFunctionBodyBlock)
41
+ return false;
42
+ const statementKinds = arrowFunctionBodyBlock.getStatements().map((statement) => statement.getKind());
43
+ // the function we're looking for has 2 for loops (a standard one and a for-in one)
44
+ const forInStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForInStatement);
45
+ const forStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForStatement);
46
+ if (forInStatements.length !== 1 || forStatements.length !== 1)
47
+ return false;
48
+ // the function we're looking for accesses its parameter three times, and it
49
+ // accesses its `modules`, `ids` and `runtime` properties (in this order)
50
+ const parameterName = functionParameters[0].getText();
51
+ const functionParameterAccessedProperties = arrowFunctionBodyBlock
52
+ .getDescendantsOfKind(ts.SyntaxKind.PropertyAccessExpression)
53
+ .filter((propertyAccessExpression) => propertyAccessExpression.getExpression().getText() === parameterName)
54
+ .map((propertyAccessExpression) => propertyAccessExpression.getName());
55
+ if (functionParameterAccessedProperties.join(", ") !== "modules, ids, runtime")
56
+ return false;
57
+ return true;
58
+ });
59
+ if (!installChunkDeclaration) {
60
+ throw new Error("ERROR: unable to find the installChunk function declaration");
61
+ }
62
+ return installChunkDeclaration;
63
+ }
64
+ /**
65
+ * Gets the declaration for what in the unminified webpack runtime file is called `installedChunks` which is an object that holds the various registered chunks.
66
+ *
67
+ * `installedChunks` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L261
68
+ *
69
+ * @param sourceFile the webpack runtime file parsed with ts-morph
70
+ * @param installChunkDeclaration the declaration for the `installChunk` variable
71
+ * @returns the `installedChunks` declaration
72
+ */
73
+ function getInstalledChunksDeclaration(sourceFile, installChunkDeclaration) {
74
+ const allVariableDeclarations = sourceFile.getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration);
75
+ const installChunkDeclarationIdx = allVariableDeclarations.findIndex((declaration) => declaration === installChunkDeclaration);
76
+ // the installedChunks declaration comes right before the installChunk one
77
+ const installedChunksDeclaration = allVariableDeclarations[installChunkDeclarationIdx - 1];
78
+ if (!installedChunksDeclaration?.getInitializer()?.isKind(ts.SyntaxKind.ObjectLiteralExpression)) {
79
+ throw new Error("ERROR: unable to find the installedChunks declaration");
80
+ }
81
+ return installedChunksDeclaration;
82
+ }
@@ -0,0 +1,20 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { describe, expect, test } from "vitest";
3
+ import { tsParseFile } from "../../../utils/index.js";
4
+ import { getChunkInstallationIdentifiers } from "./get-chunk-installation-identifiers.js";
5
+ describe("getChunkInstallationIdentifiers", () => {
6
+ test("gets chunk identifiers from unminified code", async () => {
7
+ const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, "utf8");
8
+ const tsSourceFile = tsParseFile(fileContent);
9
+ const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile);
10
+ expect(installChunk).toEqual("installChunk");
11
+ expect(installedChunks).toEqual("installedChunks");
12
+ });
13
+ test("gets chunk identifiers from minified code", async () => {
14
+ const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, "utf8");
15
+ const tsSourceFile = tsParseFile(fileContent);
16
+ const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile);
17
+ expect(installChunk).toEqual("r");
18
+ expect(installedChunks).toEqual("e");
19
+ });
20
+ });
@@ -0,0 +1,19 @@
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 declare function getFileContentWithUpdatedWebpackFRequireCode(sourceFile: ts.SourceFile, { installedChunks, installChunk }: {
17
+ installedChunks: string;
18
+ installChunk: string;
19
+ }, chunks: string[]): Promise<string>;