@opennextjs/cloudflare 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/config.d.ts +7 -1
- package/dist/api/config.js +2 -2
- package/dist/cli/build/bundle-server.d.ts +1 -1
- package/dist/cli/build/bundle-server.js +22 -38
- package/dist/cli/build/open-next/createServerBundle.js +9 -10
- package/dist/cli/build/patches/index.d.ts +0 -1
- package/dist/cli/build/patches/index.js +0 -1
- package/dist/cli/build/patches/investigated/index.d.ts +0 -1
- package/dist/cli/build/patches/investigated/index.js +0 -1
- package/dist/cli/build/patches/plugins/load-manifest.d.ts +3 -1
- package/dist/cli/build/patches/plugins/load-manifest.js +49 -7
- package/dist/cli/build/patches/plugins/next-server.d.ts +25 -0
- package/dist/cli/build/patches/plugins/next-server.js +110 -0
- package/dist/cli/build/patches/plugins/next-server.spec.js +429 -0
- package/dist/cli/build/patches/plugins/open-next.d.ts +8 -0
- package/dist/cli/build/patches/plugins/open-next.js +38 -0
- package/dist/cli/build/utils/apply-patches.js +0 -1
- package/dist/cli/templates/init.js +5 -0
- package/dist/cli/templates/shims/throw.d.ts +2 -0
- package/dist/cli/templates/shims/throw.js +2 -0
- package/package.json +3 -3
- package/dist/cli/build/patches/investigated/patch-cache.d.ts +0 -14
- package/dist/cli/build/patches/investigated/patch-cache.js +0 -40
- package/dist/cli/build/patches/plugins/build-id.d.ts +0 -6
- package/dist/cli/build/patches/plugins/build-id.js +0 -29
- package/dist/cli/build/patches/plugins/build-id.spec.js +0 -82
- package/dist/cli/build/patches/plugins/eval-manifest.d.ts +0 -7
- package/dist/cli/build/patches/plugins/eval-manifest.js +0 -61
- package/dist/cli/build/patches/to-investigate/inline-middleware-manifest.d.ts +0 -6
- package/dist/cli/build/patches/to-investigate/inline-middleware-manifest.js +0 -15
- /package/dist/cli/build/patches/plugins/{build-id.spec.d.ts → next-server.spec.d.ts} +0 -0
package/dist/api/config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BuildOptions } from "@opennextjs/aws/build/helper";
|
|
2
|
-
import { BaseOverride, LazyLoadedOverride, OpenNextConfig as AwsOpenNextConfig } from "@opennextjs/aws/types/open-next";
|
|
2
|
+
import { BaseOverride, LazyLoadedOverride, OpenNextConfig as AwsOpenNextConfig, type RoutePreloadingBehavior } from "@opennextjs/aws/types/open-next";
|
|
3
3
|
import type { CDNInvalidationHandler, IncrementalCache, Queue, TagCache } from "@opennextjs/aws/types/overrides";
|
|
4
4
|
export type Override<T extends BaseOverride> = "dummy" | T | LazyLoadedOverride<T>;
|
|
5
5
|
/**
|
|
@@ -30,6 +30,12 @@ export type CloudflareOverrides = {
|
|
|
30
30
|
* @default false
|
|
31
31
|
*/
|
|
32
32
|
enableCacheInterception?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Route preloading behavior.
|
|
35
|
+
* Using a value other than "none" can result in higher CPU usage on cold starts.
|
|
36
|
+
* @default "none"
|
|
37
|
+
*/
|
|
38
|
+
routePreloadingBehavior?: RoutePreloadingBehavior;
|
|
33
39
|
};
|
|
34
40
|
/**
|
|
35
41
|
* Defines the OpenNext configuration that targets the Cloudflare adapter
|
package/dist/api/config.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @returns the OpenNext configuration object
|
|
6
6
|
*/
|
|
7
7
|
export function defineCloudflareConfig(config = {}) {
|
|
8
|
-
const { incrementalCache, tagCache, queue, cachePurge, enableCacheInterception = false } = config;
|
|
8
|
+
const { incrementalCache, tagCache, queue, cachePurge, enableCacheInterception = false, routePreloadingBehavior = "none", } = config;
|
|
9
9
|
return {
|
|
10
10
|
default: {
|
|
11
11
|
override: {
|
|
@@ -17,7 +17,7 @@ export function defineCloudflareConfig(config = {}) {
|
|
|
17
17
|
queue: resolveQueue(queue),
|
|
18
18
|
cdnInvalidation: resolveCdnInvalidation(cachePurge),
|
|
19
19
|
},
|
|
20
|
-
routePreloadingBehavior
|
|
20
|
+
routePreloadingBehavior,
|
|
21
21
|
},
|
|
22
22
|
// node:crypto is used to compute cache keys
|
|
23
23
|
edgeExternals: ["node:crypto"],
|
|
@@ -6,7 +6,7 @@ export declare function bundleServer(buildOpts: BuildOptions): Promise<void>;
|
|
|
6
6
|
/**
|
|
7
7
|
* This function applies patches required for the code to run on workers.
|
|
8
8
|
*/
|
|
9
|
-
export declare function updateWorkerBundledCode(workerOutputFile: string
|
|
9
|
+
export declare function updateWorkerBundledCode(workerOutputFile: string): Promise<void>;
|
|
10
10
|
/**
|
|
11
11
|
* Gets the path of the worker.js file generated by the build process
|
|
12
12
|
*
|
|
@@ -9,12 +9,12 @@ import { getOpenNextConfig } from "../../api/config.js";
|
|
|
9
9
|
import { patchVercelOgLibrary } from "./patches/ast/patch-vercel-og-library.js";
|
|
10
10
|
import { patchWebpackRuntime } from "./patches/ast/webpack-runtime.js";
|
|
11
11
|
import * as patches from "./patches/index.js";
|
|
12
|
-
import { inlineBuildId } from "./patches/plugins/build-id.js";
|
|
13
12
|
import { inlineDynamicRequires } from "./patches/plugins/dynamic-requires.js";
|
|
14
|
-
import { inlineEvalManifest } from "./patches/plugins/eval-manifest.js";
|
|
15
13
|
import { inlineFindDir } from "./patches/plugins/find-dir.js";
|
|
16
14
|
import { patchInstrumentation } from "./patches/plugins/instrumentation.js";
|
|
17
15
|
import { inlineLoadManifest } from "./patches/plugins/load-manifest.js";
|
|
16
|
+
import { patchNextServer } from "./patches/plugins/next-server.js";
|
|
17
|
+
import { patchResolveCache } from "./patches/plugins/open-next.js";
|
|
18
18
|
import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js";
|
|
19
19
|
import { patchPagesRouterContext } from "./patches/plugins/pages-router-context.js";
|
|
20
20
|
import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js";
|
|
@@ -42,7 +42,7 @@ const optionalDependencies = [
|
|
|
42
42
|
*/
|
|
43
43
|
export async function bundleServer(buildOpts) {
|
|
44
44
|
patches.copyPackageCliFiles(packageDistDir, buildOpts);
|
|
45
|
-
const { appPath, outputDir, monorepoRoot } = buildOpts;
|
|
45
|
+
const { appPath, outputDir, monorepoRoot, debug } = buildOpts;
|
|
46
46
|
const baseManifestPath = path.join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
|
|
47
47
|
const serverFiles = path.join(baseManifestPath, "required-server-files.json");
|
|
48
48
|
const nextConfig = JSON.parse(fs.readFileSync(serverFiles, "utf-8")).config;
|
|
@@ -60,7 +60,11 @@ export async function bundleServer(buildOpts) {
|
|
|
60
60
|
outfile: openNextServerBundle,
|
|
61
61
|
format: "esm",
|
|
62
62
|
target: "esnext",
|
|
63
|
-
|
|
63
|
+
// Minify code as much as possible but stay safe by not renaming identifiers
|
|
64
|
+
minifyWhitespace: !debug,
|
|
65
|
+
minifyIdentifiers: false,
|
|
66
|
+
minifySyntax: !debug,
|
|
67
|
+
legalComments: "none",
|
|
64
68
|
metafile: true,
|
|
65
69
|
// Next traces files using the default conditions from `nft` (`node`, `require`, `import` and `default`)
|
|
66
70
|
//
|
|
@@ -80,31 +84,28 @@ export async function bundleServer(buildOpts) {
|
|
|
80
84
|
handleOptionalDependencies(optionalDependencies),
|
|
81
85
|
patchInstrumentation(updater, buildOpts),
|
|
82
86
|
patchPagesRouterContext(buildOpts),
|
|
83
|
-
inlineEvalManifest(updater, buildOpts),
|
|
84
87
|
inlineFindDir(updater, buildOpts),
|
|
85
88
|
inlineLoadManifest(updater, buildOpts),
|
|
86
|
-
|
|
89
|
+
patchNextServer(updater, buildOpts),
|
|
87
90
|
patchDepdDeprecations(updater),
|
|
91
|
+
patchResolveCache(updater, buildOpts),
|
|
88
92
|
// Apply updater updates, must be the last plugin
|
|
89
93
|
updater.plugin,
|
|
90
94
|
],
|
|
91
95
|
external: ["./middleware/handler.mjs"],
|
|
92
96
|
alias: {
|
|
93
|
-
//
|
|
94
|
-
// but just to be safe we replace it with a module that re-exports the native fetch
|
|
95
|
-
// we do this to both save on bundle size (there isn't really any benefit in us shipping the node-fetch code)
|
|
96
|
-
// and also get rid of a warning in the terminal caused by the package (because it performs an === comparison with -0)
|
|
97
|
+
// Workers have `fetch` so the `node-fetch` polyfill is not needed
|
|
97
98
|
"next/dist/compiled/node-fetch": path.join(buildOpts.outputDir, "cloudflare-templates/shims/fetch.js"),
|
|
98
|
-
//
|
|
99
|
-
// eval("require")("bufferutil");
|
|
100
|
-
// eval("require")("utf-8-validate");
|
|
99
|
+
// Workers have builtin Web Sockets
|
|
101
100
|
"next/dist/compiled/ws": path.join(buildOpts.outputDir, "cloudflare-templates/shims/empty.js"),
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
101
|
+
// The toolbox optimizer pulls severals MB of dependencies (`caniuse-lite`, `terser`, `acorn`, ...)
|
|
102
|
+
// Drop it to optimize the code size
|
|
103
|
+
// See https://github.com/vercel/next.js/blob/6eb235c/packages/next/src/server/optimize-amp.ts
|
|
104
|
+
"next/dist/compiled/@ampproject/toolbox-optimizer": path.join(buildOpts.outputDir, "cloudflare-templates/shims/throw.js"),
|
|
105
|
+
// The edge runtime is not supported
|
|
105
106
|
"next/dist/compiled/edge-runtime": path.join(buildOpts.outputDir, "cloudflare-templates/shims/empty.js"),
|
|
106
|
-
// `@next/env` is
|
|
107
|
-
//
|
|
107
|
+
// `@next/env` is used by Next to load environment variables from files.
|
|
108
|
+
// OpenNext inlines the values at build time so this is not needed.
|
|
108
109
|
"@next/env": path.join(buildOpts.outputDir, "cloudflare-templates/shims/env.js"),
|
|
109
110
|
},
|
|
110
111
|
define: {
|
|
@@ -133,7 +134,7 @@ export async function bundleServer(buildOpts) {
|
|
|
133
134
|
platform: "node",
|
|
134
135
|
});
|
|
135
136
|
fs.writeFileSync(openNextServerBundle + ".meta.json", JSON.stringify(result.metafile, null, 2));
|
|
136
|
-
await updateWorkerBundledCode(openNextServerBundle
|
|
137
|
+
await updateWorkerBundledCode(openNextServerBundle);
|
|
137
138
|
const isMonorepo = monorepoRoot !== appPath;
|
|
138
139
|
if (isMonorepo) {
|
|
139
140
|
fs.writeFileSync(path.join(outputPath, "handler.mjs"), `export { handler } from "./${normalizePath(packagePath)}/handler.mjs";`);
|
|
@@ -143,27 +144,10 @@ export async function bundleServer(buildOpts) {
|
|
|
143
144
|
/**
|
|
144
145
|
* This function applies patches required for the code to run on workers.
|
|
145
146
|
*/
|
|
146
|
-
export async function updateWorkerBundledCode(workerOutputFile
|
|
147
|
+
export async function updateWorkerBundledCode(workerOutputFile) {
|
|
147
148
|
const code = await readFile(workerOutputFile, "utf8");
|
|
148
149
|
const patchedCode = await patchCodeWithValidations(code, [
|
|
149
|
-
["require", patches.patchRequire],
|
|
150
|
-
["cacheHandler", (code) => patches.patchCache(code, buildOpts)],
|
|
151
|
-
["composableCache", (code) => patches.patchComposableCache(code, buildOpts), { isOptional: true }],
|
|
152
|
-
[
|
|
153
|
-
"'require(this.middlewareManifestPath)'",
|
|
154
|
-
(code) => patches.inlineMiddlewareManifestRequire(code, buildOpts),
|
|
155
|
-
{ isOptional: true },
|
|
156
|
-
],
|
|
157
|
-
[
|
|
158
|
-
"`require.resolve` call",
|
|
159
|
-
// workers do not support dynamic require nor require.resolve
|
|
160
|
-
(code) => code.replace('require.resolve("./cache.cjs")', '"unused"'),
|
|
161
|
-
],
|
|
162
|
-
[
|
|
163
|
-
"`require.resolve composable cache` call",
|
|
164
|
-
// workers do not support dynamic require nor require.resolve
|
|
165
|
-
(code) => code.replace('require.resolve("./composable-cache.cjs")', '"unused"'),
|
|
166
|
-
],
|
|
150
|
+
["require", patches.patchRequire, { isOptional: true }],
|
|
167
151
|
]);
|
|
168
152
|
await writeFile(workerOutputFile, patchedCode);
|
|
169
153
|
}
|
|
@@ -10,9 +10,7 @@ import { copyMiddlewareResources, generateEdgeBundle } from "@opennextjs/aws/bui
|
|
|
10
10
|
import * as buildHelper from "@opennextjs/aws/build/helper.js";
|
|
11
11
|
import { installDependencies } from "@opennextjs/aws/build/installDeps.js";
|
|
12
12
|
import { applyCodePatches } from "@opennextjs/aws/build/patch/codePatcher.js";
|
|
13
|
-
import
|
|
14
|
-
// TODO: import from patches/index.js when https://github.com/opennextjs/opennextjs-aws/pull/827 is released
|
|
15
|
-
import { patchBackgroundRevalidation } from "@opennextjs/aws/build/patch/patches/patchBackgroundRevalidation.js";
|
|
13
|
+
import * as awsPatches from "@opennextjs/aws/build/patch/patches/index.js";
|
|
16
14
|
import logger from "@opennextjs/aws/logger.js";
|
|
17
15
|
import { minifyAll } from "@opennextjs/aws/minimize-js.js";
|
|
18
16
|
import { openNextEdgePlugins } from "@opennextjs/aws/plugins/edge.js";
|
|
@@ -133,13 +131,14 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
133
131
|
}
|
|
134
132
|
const additionalCodePatches = codeCustomization?.additionalCodePatches ?? [];
|
|
135
133
|
await applyCodePatches(options, tracedFiles, manifests, [
|
|
136
|
-
patchFetchCacheSetMissingWaitUntil,
|
|
137
|
-
patchFetchCacheForISR,
|
|
138
|
-
patchUnstableCacheForISR,
|
|
139
|
-
patchUseCacheForISR,
|
|
140
|
-
patchNextServer,
|
|
141
|
-
patchEnvVars,
|
|
142
|
-
patchBackgroundRevalidation,
|
|
134
|
+
awsPatches.patchFetchCacheSetMissingWaitUntil,
|
|
135
|
+
awsPatches.patchFetchCacheForISR,
|
|
136
|
+
awsPatches.patchUnstableCacheForISR,
|
|
137
|
+
awsPatches.patchUseCacheForISR,
|
|
138
|
+
awsPatches.patchNextServer,
|
|
139
|
+
awsPatches.patchEnvVars,
|
|
140
|
+
awsPatches.patchBackgroundRevalidation,
|
|
141
|
+
awsPatches.patchDropBabel,
|
|
143
142
|
// Cloudflare specific patches
|
|
144
143
|
patchResRevalidate,
|
|
145
144
|
patchUseCacheIO,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `loadManifest`
|
|
2
|
+
* Inline `loadManifest` and `evalManifest` from `load-manifest.js`
|
|
3
|
+
*
|
|
4
|
+
* They rely on `readFileSync` that is not supported by workerd.
|
|
3
5
|
*/
|
|
4
6
|
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
5
7
|
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `loadManifest`
|
|
2
|
+
* Inline `loadManifest` and `evalManifest` from `load-manifest.js`
|
|
3
|
+
*
|
|
4
|
+
* They rely on `readFileSync` that is not supported by workerd.
|
|
3
5
|
*/
|
|
4
6
|
import { readFile } from "node:fs/promises";
|
|
5
7
|
import { join, posix, relative, sep } from "node:path";
|
|
@@ -16,21 +18,24 @@ export function inlineLoadManifest(updater, buildOpts) {
|
|
|
16
18
|
escape: false,
|
|
17
19
|
}),
|
|
18
20
|
contentFilter: /function loadManifest\(/,
|
|
19
|
-
callback: async ({ contents }) =>
|
|
21
|
+
callback: async ({ contents }) => {
|
|
22
|
+
contents = await patchCode(contents, await getLoadManifestRule(buildOpts));
|
|
23
|
+
contents = await patchCode(contents, await getEvalManifestRule(buildOpts));
|
|
24
|
+
return contents;
|
|
25
|
+
},
|
|
20
26
|
},
|
|
21
27
|
},
|
|
22
28
|
]);
|
|
23
29
|
}
|
|
24
|
-
async function
|
|
30
|
+
async function getLoadManifestRule(buildOpts) {
|
|
25
31
|
const { outputDir } = buildOpts;
|
|
26
32
|
const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts));
|
|
27
33
|
const dotNextDir = join(baseDir, ".next");
|
|
28
34
|
const manifests = await glob(join(dotNextDir, "**/*-manifest.json"), { windowsPathsNoEscape: true });
|
|
29
35
|
const returnManifests = (await Promise.all(manifests.map(async (manifest) => `
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`))).join("\n");
|
|
36
|
+
if ($PATH.endsWith("${normalizePath("/" + relative(dotNextDir, manifest))}")) {
|
|
37
|
+
return ${await readFile(manifest, "utf-8")};
|
|
38
|
+
}`))).join("\n");
|
|
34
39
|
return {
|
|
35
40
|
rule: {
|
|
36
41
|
pattern: `
|
|
@@ -46,3 +51,40 @@ function loadManifest($PATH, $$$ARGS) {
|
|
|
46
51
|
}`,
|
|
47
52
|
};
|
|
48
53
|
}
|
|
54
|
+
async function getEvalManifestRule(buildOpts) {
|
|
55
|
+
const { outputDir } = buildOpts;
|
|
56
|
+
const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
|
|
57
|
+
const appDir = join(baseDir, "server/app");
|
|
58
|
+
const manifests = await glob(join(baseDir, "**/*_client-reference-manifest.js"), {
|
|
59
|
+
windowsPathsNoEscape: true,
|
|
60
|
+
});
|
|
61
|
+
const returnManifests = manifests
|
|
62
|
+
.map((manifest) => {
|
|
63
|
+
const endsWith = normalizePath(relative(baseDir, manifest));
|
|
64
|
+
const key = normalizePath("/" + relative(appDir, manifest)).replace("_client-reference-manifest.js", "");
|
|
65
|
+
return `
|
|
66
|
+
if ($PATH.endsWith("${endsWith}")) {
|
|
67
|
+
require(${JSON.stringify(manifest)});
|
|
68
|
+
return {
|
|
69
|
+
__RSC_MANIFEST: {
|
|
70
|
+
"${key}": globalThis.__RSC_MANIFEST["${key}"],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}`;
|
|
74
|
+
})
|
|
75
|
+
.join("\n");
|
|
76
|
+
return {
|
|
77
|
+
rule: {
|
|
78
|
+
pattern: `
|
|
79
|
+
function evalManifest($PATH, $$$ARGS) {
|
|
80
|
+
$$$_
|
|
81
|
+
}`,
|
|
82
|
+
},
|
|
83
|
+
fix: `
|
|
84
|
+
function evalManifest($PATH, $$$ARGS) {
|
|
85
|
+
$PATH = $PATH.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)});
|
|
86
|
+
${returnManifests}
|
|
87
|
+
throw new Error(\`Unexpected evalManifest(\${$PATH}) call!\`);
|
|
88
|
+
}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Misc patches for `next-server.js`
|
|
3
|
+
*
|
|
4
|
+
* Note: we will probably need to revisit the patches when the Next adapter API lands
|
|
5
|
+
*
|
|
6
|
+
* - Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd
|
|
7
|
+
* - Inline the middleware manifest
|
|
8
|
+
* - Override the cache and composable cache handlers
|
|
9
|
+
*/
|
|
10
|
+
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
11
|
+
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
12
|
+
export declare function patchNextServer(updater: ContentUpdater, buildOpts: BuildOptions): Plugin;
|
|
13
|
+
export declare const buildIdRule = "\nrule:\n pattern:\n selector: method_definition\n context: \"class { getBuildId($$$PARAMS) { $$$_ } }\"\nfix: |-\n getBuildId($$$PARAMS) {\n return process.env.NEXT_BUILD_ID;\n }\n";
|
|
14
|
+
export declare function createMiddlewareManifestRule(manifest: unknown): string;
|
|
15
|
+
/**
|
|
16
|
+
* The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
|
|
17
|
+
* Next.js would then do a dynamic require on a transformed version of the path to retrieve the
|
|
18
|
+
* cache handler and create a new instance of it.
|
|
19
|
+
*
|
|
20
|
+
* This is problematic in workerd due to the dynamic import of the file that is not known from
|
|
21
|
+
* build-time. Therefore, we have to manually override the default way that the cache handler is
|
|
22
|
+
* instantiated with a dynamic require that uses a string literal for the path.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createCacheHandlerRule(handlerPath: string): string;
|
|
25
|
+
export declare function createComposableCacheHandlersRule(handlerPath: string): string;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Misc patches for `next-server.js`
|
|
3
|
+
*
|
|
4
|
+
* Note: we will probably need to revisit the patches when the Next adapter API lands
|
|
5
|
+
*
|
|
6
|
+
* - Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd
|
|
7
|
+
* - Inline the middleware manifest
|
|
8
|
+
* - Override the cache and composable cache handlers
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
13
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
14
|
+
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
15
|
+
import { normalizePath } from "../../utils/index.js";
|
|
16
|
+
export function patchNextServer(updater, buildOpts) {
|
|
17
|
+
return updater.updateContent("next-server", [
|
|
18
|
+
{
|
|
19
|
+
field: {
|
|
20
|
+
filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/next-server\.js$`, {
|
|
21
|
+
escape: false,
|
|
22
|
+
}),
|
|
23
|
+
contentFilter: /getBuildId\(/,
|
|
24
|
+
callback: async ({ contents }) => {
|
|
25
|
+
const { outputDir } = buildOpts;
|
|
26
|
+
contents = patchCode(contents, buildIdRule);
|
|
27
|
+
const manifestPath = path.join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server/middleware-manifest.json");
|
|
28
|
+
const manifest = existsSync(manifestPath)
|
|
29
|
+
? JSON.parse(await readFileSync(manifestPath, "utf-8"))
|
|
30
|
+
: {};
|
|
31
|
+
contents = patchCode(contents, createMiddlewareManifestRule(manifest));
|
|
32
|
+
const outputPath = path.join(outputDir, "server-functions/default");
|
|
33
|
+
const cacheHandler = path.join(outputPath, getPackagePath(buildOpts), "cache.cjs");
|
|
34
|
+
contents = patchCode(contents, createCacheHandlerRule(cacheHandler));
|
|
35
|
+
const composableCacheHandler = path.join(outputPath, getPackagePath(buildOpts), "composable-cache.cjs");
|
|
36
|
+
contents = patchCode(contents, createComposableCacheHandlersRule(composableCacheHandler));
|
|
37
|
+
return contents;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
export const buildIdRule = `
|
|
44
|
+
rule:
|
|
45
|
+
pattern:
|
|
46
|
+
selector: method_definition
|
|
47
|
+
context: "class { getBuildId($$$PARAMS) { $$$_ } }"
|
|
48
|
+
fix: |-
|
|
49
|
+
getBuildId($$$PARAMS) {
|
|
50
|
+
return process.env.NEXT_BUILD_ID;
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
export function createMiddlewareManifestRule(manifest) {
|
|
54
|
+
return `
|
|
55
|
+
rule:
|
|
56
|
+
pattern:
|
|
57
|
+
selector: method_definition
|
|
58
|
+
context: "class { getMiddlewareManifest($$$PARAMS) { $$$_ } }"
|
|
59
|
+
fix: |-
|
|
60
|
+
getMiddlewareManifest($$$PARAMS) {
|
|
61
|
+
return ${JSON.stringify(manifest)};
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
|
|
67
|
+
* Next.js would then do a dynamic require on a transformed version of the path to retrieve the
|
|
68
|
+
* cache handler and create a new instance of it.
|
|
69
|
+
*
|
|
70
|
+
* This is problematic in workerd due to the dynamic import of the file that is not known from
|
|
71
|
+
* build-time. Therefore, we have to manually override the default way that the cache handler is
|
|
72
|
+
* instantiated with a dynamic require that uses a string literal for the path.
|
|
73
|
+
*/
|
|
74
|
+
export function createCacheHandlerRule(handlerPath) {
|
|
75
|
+
return `
|
|
76
|
+
rule:
|
|
77
|
+
pattern: "const { cacheHandler } = this.nextConfig;"
|
|
78
|
+
inside:
|
|
79
|
+
kind: method_definition
|
|
80
|
+
has:
|
|
81
|
+
field: name
|
|
82
|
+
regex: ^getIncrementalCache$
|
|
83
|
+
stopBy: end
|
|
84
|
+
|
|
85
|
+
fix: |-
|
|
86
|
+
const cacheHandler = null;
|
|
87
|
+
CacheHandler = require('${normalizePath(handlerPath)}').default;
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
export function createComposableCacheHandlersRule(handlerPath) {
|
|
91
|
+
return `
|
|
92
|
+
rule:
|
|
93
|
+
pattern: "const { cacheHandlers } = this.nextConfig.experimental;"
|
|
94
|
+
inside:
|
|
95
|
+
kind: method_definition
|
|
96
|
+
has:
|
|
97
|
+
field: name
|
|
98
|
+
regex: ^loadCustomCacheHandlers$
|
|
99
|
+
stopBy: end
|
|
100
|
+
|
|
101
|
+
fix: |-
|
|
102
|
+
const cacheHandlers = null;
|
|
103
|
+
const handlersSymbol = Symbol.for('@next/cache-handlers');
|
|
104
|
+
const handlersMapSymbol = Symbol.for('@next/cache-handlers-map');
|
|
105
|
+
const handlersSetSymbol = Symbol.for('@next/cache-handlers-set');
|
|
106
|
+
globalThis[handlersMapSymbol] = new Map();
|
|
107
|
+
globalThis[handlersMapSymbol].set("default", require('${normalizePath(handlerPath)}').default);
|
|
108
|
+
globalThis[handlersSetSymbol] = new Set(globalThis[handlersMapSymbol].values());
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { buildIdRule, createCacheHandlerRule, createComposableCacheHandlersRule, createMiddlewareManifestRule, } from "./next-server.js";
|
|
4
|
+
describe("Next Server", () => {
|
|
5
|
+
const nextServerCode = `
|
|
6
|
+
class NextNodeServer extends _baseserver.default {
|
|
7
|
+
constructor(options){
|
|
8
|
+
// Initialize super class
|
|
9
|
+
super(options);
|
|
10
|
+
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
11
|
+
}
|
|
12
|
+
async handleUpgrade() {
|
|
13
|
+
// The web server does not support web sockets, it's only used for HMR in
|
|
14
|
+
// development.
|
|
15
|
+
}
|
|
16
|
+
loadEnvConfig({ dev, forceReload, silent }) {
|
|
17
|
+
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
18
|
+
info: ()=>{},
|
|
19
|
+
error: ()=>{}
|
|
20
|
+
} : _log, forceReload);
|
|
21
|
+
}
|
|
22
|
+
async hasPage(pathname) {
|
|
23
|
+
var _this_nextConfig_i18n;
|
|
24
|
+
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
25
|
+
}
|
|
26
|
+
getBuildId() {
|
|
27
|
+
const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE);
|
|
28
|
+
try {
|
|
29
|
+
return _fs.default.readFileSync(buildIdFile, "utf8").trim();
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.code === "ENOENT") {
|
|
32
|
+
throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`);
|
|
33
|
+
}
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
getMiddlewareManifest() {
|
|
38
|
+
if (this.minimalMode) return null;
|
|
39
|
+
const manifest = require(this.middlewareManifestPath);
|
|
40
|
+
return manifest;
|
|
41
|
+
}
|
|
42
|
+
async loadCustomCacheHandlers() {
|
|
43
|
+
const { cacheHandlers } = this.nextConfig.experimental;
|
|
44
|
+
if (!cacheHandlers) return;
|
|
45
|
+
// If we've already initialized the cache handlers interface, don't do it
|
|
46
|
+
// again.
|
|
47
|
+
if (!(0, _handlers.initializeCacheHandlers)()) return;
|
|
48
|
+
for (const [kind, handler] of Object.entries(cacheHandlers)){
|
|
49
|
+
if (!handler) continue;
|
|
50
|
+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async getIncrementalCache({ requestHeaders, requestProtocol }) {
|
|
54
|
+
const dev = !!this.renderOpts.dev;
|
|
55
|
+
let CacheHandler;
|
|
56
|
+
const { cacheHandler } = this.nextConfig;
|
|
57
|
+
if (cacheHandler) {
|
|
58
|
+
CacheHandler = (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, cacheHandler)));
|
|
59
|
+
}
|
|
60
|
+
await this.loadCustomCacheHandlers();
|
|
61
|
+
// incremental-cache is request specific
|
|
62
|
+
// although can have shared caches in module scope
|
|
63
|
+
// per-cache handler
|
|
64
|
+
return new _incrementalcache.IncrementalCache({
|
|
65
|
+
fs: this.getCacheFilesystem(),
|
|
66
|
+
dev,
|
|
67
|
+
requestHeaders,
|
|
68
|
+
requestProtocol,
|
|
69
|
+
allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys,
|
|
70
|
+
minimalMode: this.minimalMode,
|
|
71
|
+
serverDistDir: this.serverDistDir,
|
|
72
|
+
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
|
|
73
|
+
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
|
|
74
|
+
flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk,
|
|
75
|
+
getPrerenderManifest: ()=>this.getPrerenderManifest(),
|
|
76
|
+
CurCacheHandler: CacheHandler
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
getEnabledDirectories(dev) {
|
|
80
|
+
const dir = dev ? this.dir : this.serverDistDir;
|
|
81
|
+
return {
|
|
82
|
+
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
83
|
+
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// ...
|
|
87
|
+
}`;
|
|
88
|
+
test("build ID", () => {
|
|
89
|
+
expect(patchCode(nextServerCode, buildIdRule)).toMatchInlineSnapshot(`
|
|
90
|
+
"class NextNodeServer extends _baseserver.default {
|
|
91
|
+
constructor(options){
|
|
92
|
+
// Initialize super class
|
|
93
|
+
super(options);
|
|
94
|
+
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
95
|
+
}
|
|
96
|
+
async handleUpgrade() {
|
|
97
|
+
// The web server does not support web sockets, it's only used for HMR in
|
|
98
|
+
// development.
|
|
99
|
+
}
|
|
100
|
+
loadEnvConfig({ dev, forceReload, silent }) {
|
|
101
|
+
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
102
|
+
info: ()=>{},
|
|
103
|
+
error: ()=>{}
|
|
104
|
+
} : _log, forceReload);
|
|
105
|
+
}
|
|
106
|
+
async hasPage(pathname) {
|
|
107
|
+
var _this_nextConfig_i18n;
|
|
108
|
+
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
109
|
+
}
|
|
110
|
+
getBuildId() {
|
|
111
|
+
return process.env.NEXT_BUILD_ID;
|
|
112
|
+
}
|
|
113
|
+
getMiddlewareManifest() {
|
|
114
|
+
if (this.minimalMode) return null;
|
|
115
|
+
const manifest = require(this.middlewareManifestPath);
|
|
116
|
+
return manifest;
|
|
117
|
+
}
|
|
118
|
+
async loadCustomCacheHandlers() {
|
|
119
|
+
const { cacheHandlers } = this.nextConfig.experimental;
|
|
120
|
+
if (!cacheHandlers) return;
|
|
121
|
+
// If we've already initialized the cache handlers interface, don't do it
|
|
122
|
+
// again.
|
|
123
|
+
if (!(0, _handlers.initializeCacheHandlers)()) return;
|
|
124
|
+
for (const [kind, handler] of Object.entries(cacheHandlers)){
|
|
125
|
+
if (!handler) continue;
|
|
126
|
+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async getIncrementalCache({ requestHeaders, requestProtocol }) {
|
|
130
|
+
const dev = !!this.renderOpts.dev;
|
|
131
|
+
let CacheHandler;
|
|
132
|
+
const { cacheHandler } = this.nextConfig;
|
|
133
|
+
if (cacheHandler) {
|
|
134
|
+
CacheHandler = (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, cacheHandler)));
|
|
135
|
+
}
|
|
136
|
+
await this.loadCustomCacheHandlers();
|
|
137
|
+
// incremental-cache is request specific
|
|
138
|
+
// although can have shared caches in module scope
|
|
139
|
+
// per-cache handler
|
|
140
|
+
return new _incrementalcache.IncrementalCache({
|
|
141
|
+
fs: this.getCacheFilesystem(),
|
|
142
|
+
dev,
|
|
143
|
+
requestHeaders,
|
|
144
|
+
requestProtocol,
|
|
145
|
+
allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys,
|
|
146
|
+
minimalMode: this.minimalMode,
|
|
147
|
+
serverDistDir: this.serverDistDir,
|
|
148
|
+
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
|
|
149
|
+
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
|
|
150
|
+
flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk,
|
|
151
|
+
getPrerenderManifest: ()=>this.getPrerenderManifest(),
|
|
152
|
+
CurCacheHandler: CacheHandler
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
getEnabledDirectories(dev) {
|
|
156
|
+
const dir = dev ? this.dir : this.serverDistDir;
|
|
157
|
+
return {
|
|
158
|
+
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
159
|
+
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// ...
|
|
163
|
+
}"
|
|
164
|
+
`);
|
|
165
|
+
});
|
|
166
|
+
test("middleware manifest", () => {
|
|
167
|
+
expect(patchCode(nextServerCode, createMiddlewareManifestRule("manifest"))).toMatchInlineSnapshot(`
|
|
168
|
+
"class NextNodeServer extends _baseserver.default {
|
|
169
|
+
constructor(options){
|
|
170
|
+
// Initialize super class
|
|
171
|
+
super(options);
|
|
172
|
+
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
173
|
+
}
|
|
174
|
+
async handleUpgrade() {
|
|
175
|
+
// The web server does not support web sockets, it's only used for HMR in
|
|
176
|
+
// development.
|
|
177
|
+
}
|
|
178
|
+
loadEnvConfig({ dev, forceReload, silent }) {
|
|
179
|
+
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
180
|
+
info: ()=>{},
|
|
181
|
+
error: ()=>{}
|
|
182
|
+
} : _log, forceReload);
|
|
183
|
+
}
|
|
184
|
+
async hasPage(pathname) {
|
|
185
|
+
var _this_nextConfig_i18n;
|
|
186
|
+
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
187
|
+
}
|
|
188
|
+
getBuildId() {
|
|
189
|
+
const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE);
|
|
190
|
+
try {
|
|
191
|
+
return _fs.default.readFileSync(buildIdFile, "utf8").trim();
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (err.code === "ENOENT") {
|
|
194
|
+
throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`);
|
|
195
|
+
}
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
getMiddlewareManifest() {
|
|
200
|
+
return "manifest";
|
|
201
|
+
}
|
|
202
|
+
async loadCustomCacheHandlers() {
|
|
203
|
+
const { cacheHandlers } = this.nextConfig.experimental;
|
|
204
|
+
if (!cacheHandlers) return;
|
|
205
|
+
// If we've already initialized the cache handlers interface, don't do it
|
|
206
|
+
// again.
|
|
207
|
+
if (!(0, _handlers.initializeCacheHandlers)()) return;
|
|
208
|
+
for (const [kind, handler] of Object.entries(cacheHandlers)){
|
|
209
|
+
if (!handler) continue;
|
|
210
|
+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async getIncrementalCache({ requestHeaders, requestProtocol }) {
|
|
214
|
+
const dev = !!this.renderOpts.dev;
|
|
215
|
+
let CacheHandler;
|
|
216
|
+
const { cacheHandler } = this.nextConfig;
|
|
217
|
+
if (cacheHandler) {
|
|
218
|
+
CacheHandler = (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, cacheHandler)));
|
|
219
|
+
}
|
|
220
|
+
await this.loadCustomCacheHandlers();
|
|
221
|
+
// incremental-cache is request specific
|
|
222
|
+
// although can have shared caches in module scope
|
|
223
|
+
// per-cache handler
|
|
224
|
+
return new _incrementalcache.IncrementalCache({
|
|
225
|
+
fs: this.getCacheFilesystem(),
|
|
226
|
+
dev,
|
|
227
|
+
requestHeaders,
|
|
228
|
+
requestProtocol,
|
|
229
|
+
allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys,
|
|
230
|
+
minimalMode: this.minimalMode,
|
|
231
|
+
serverDistDir: this.serverDistDir,
|
|
232
|
+
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
|
|
233
|
+
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
|
|
234
|
+
flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk,
|
|
235
|
+
getPrerenderManifest: ()=>this.getPrerenderManifest(),
|
|
236
|
+
CurCacheHandler: CacheHandler
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
getEnabledDirectories(dev) {
|
|
240
|
+
const dir = dev ? this.dir : this.serverDistDir;
|
|
241
|
+
return {
|
|
242
|
+
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
243
|
+
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// ...
|
|
247
|
+
}"
|
|
248
|
+
`);
|
|
249
|
+
});
|
|
250
|
+
test("cache handler", () => {
|
|
251
|
+
expect(patchCode(nextServerCode, createCacheHandlerRule("manifest"))).toMatchInlineSnapshot(`
|
|
252
|
+
"class NextNodeServer extends _baseserver.default {
|
|
253
|
+
constructor(options){
|
|
254
|
+
// Initialize super class
|
|
255
|
+
super(options);
|
|
256
|
+
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
257
|
+
}
|
|
258
|
+
async handleUpgrade() {
|
|
259
|
+
// The web server does not support web sockets, it's only used for HMR in
|
|
260
|
+
// development.
|
|
261
|
+
}
|
|
262
|
+
loadEnvConfig({ dev, forceReload, silent }) {
|
|
263
|
+
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
264
|
+
info: ()=>{},
|
|
265
|
+
error: ()=>{}
|
|
266
|
+
} : _log, forceReload);
|
|
267
|
+
}
|
|
268
|
+
async hasPage(pathname) {
|
|
269
|
+
var _this_nextConfig_i18n;
|
|
270
|
+
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
271
|
+
}
|
|
272
|
+
getBuildId() {
|
|
273
|
+
const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE);
|
|
274
|
+
try {
|
|
275
|
+
return _fs.default.readFileSync(buildIdFile, "utf8").trim();
|
|
276
|
+
} catch (err) {
|
|
277
|
+
if (err.code === "ENOENT") {
|
|
278
|
+
throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`);
|
|
279
|
+
}
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
getMiddlewareManifest() {
|
|
284
|
+
if (this.minimalMode) return null;
|
|
285
|
+
const manifest = require(this.middlewareManifestPath);
|
|
286
|
+
return manifest;
|
|
287
|
+
}
|
|
288
|
+
async loadCustomCacheHandlers() {
|
|
289
|
+
const { cacheHandlers } = this.nextConfig.experimental;
|
|
290
|
+
if (!cacheHandlers) return;
|
|
291
|
+
// If we've already initialized the cache handlers interface, don't do it
|
|
292
|
+
// again.
|
|
293
|
+
if (!(0, _handlers.initializeCacheHandlers)()) return;
|
|
294
|
+
for (const [kind, handler] of Object.entries(cacheHandlers)){
|
|
295
|
+
if (!handler) continue;
|
|
296
|
+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async getIncrementalCache({ requestHeaders, requestProtocol }) {
|
|
300
|
+
const dev = !!this.renderOpts.dev;
|
|
301
|
+
let CacheHandler;
|
|
302
|
+
const cacheHandler = null;
|
|
303
|
+
CacheHandler = require('manifest').default;
|
|
304
|
+
if (cacheHandler) {
|
|
305
|
+
CacheHandler = (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, cacheHandler)));
|
|
306
|
+
}
|
|
307
|
+
await this.loadCustomCacheHandlers();
|
|
308
|
+
// incremental-cache is request specific
|
|
309
|
+
// although can have shared caches in module scope
|
|
310
|
+
// per-cache handler
|
|
311
|
+
return new _incrementalcache.IncrementalCache({
|
|
312
|
+
fs: this.getCacheFilesystem(),
|
|
313
|
+
dev,
|
|
314
|
+
requestHeaders,
|
|
315
|
+
requestProtocol,
|
|
316
|
+
allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys,
|
|
317
|
+
minimalMode: this.minimalMode,
|
|
318
|
+
serverDistDir: this.serverDistDir,
|
|
319
|
+
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
|
|
320
|
+
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
|
|
321
|
+
flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk,
|
|
322
|
+
getPrerenderManifest: ()=>this.getPrerenderManifest(),
|
|
323
|
+
CurCacheHandler: CacheHandler
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
getEnabledDirectories(dev) {
|
|
327
|
+
const dir = dev ? this.dir : this.serverDistDir;
|
|
328
|
+
return {
|
|
329
|
+
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
330
|
+
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// ...
|
|
334
|
+
}"
|
|
335
|
+
`);
|
|
336
|
+
});
|
|
337
|
+
test("composable cache handler", () => {
|
|
338
|
+
expect(patchCode(nextServerCode, createComposableCacheHandlersRule("manifest"))).toMatchInlineSnapshot(`
|
|
339
|
+
"class NextNodeServer extends _baseserver.default {
|
|
340
|
+
constructor(options){
|
|
341
|
+
// Initialize super class
|
|
342
|
+
super(options);
|
|
343
|
+
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
344
|
+
}
|
|
345
|
+
async handleUpgrade() {
|
|
346
|
+
// The web server does not support web sockets, it's only used for HMR in
|
|
347
|
+
// development.
|
|
348
|
+
}
|
|
349
|
+
loadEnvConfig({ dev, forceReload, silent }) {
|
|
350
|
+
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
351
|
+
info: ()=>{},
|
|
352
|
+
error: ()=>{}
|
|
353
|
+
} : _log, forceReload);
|
|
354
|
+
}
|
|
355
|
+
async hasPage(pathname) {
|
|
356
|
+
var _this_nextConfig_i18n;
|
|
357
|
+
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
358
|
+
}
|
|
359
|
+
getBuildId() {
|
|
360
|
+
const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE);
|
|
361
|
+
try {
|
|
362
|
+
return _fs.default.readFileSync(buildIdFile, "utf8").trim();
|
|
363
|
+
} catch (err) {
|
|
364
|
+
if (err.code === "ENOENT") {
|
|
365
|
+
throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`);
|
|
366
|
+
}
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
getMiddlewareManifest() {
|
|
371
|
+
if (this.minimalMode) return null;
|
|
372
|
+
const manifest = require(this.middlewareManifestPath);
|
|
373
|
+
return manifest;
|
|
374
|
+
}
|
|
375
|
+
async loadCustomCacheHandlers() {
|
|
376
|
+
const cacheHandlers = null;
|
|
377
|
+
const handlersSymbol = Symbol.for('@next/cache-handlers');
|
|
378
|
+
const handlersMapSymbol = Symbol.for('@next/cache-handlers-map');
|
|
379
|
+
const handlersSetSymbol = Symbol.for('@next/cache-handlers-set');
|
|
380
|
+
globalThis[handlersMapSymbol] = new Map();
|
|
381
|
+
globalThis[handlersMapSymbol].set("default", require('manifest').default);
|
|
382
|
+
globalThis[handlersSetSymbol] = new Set(globalThis[handlersMapSymbol].values());
|
|
383
|
+
if (!cacheHandlers) return;
|
|
384
|
+
// If we've already initialized the cache handlers interface, don't do it
|
|
385
|
+
// again.
|
|
386
|
+
if (!(0, _handlers.initializeCacheHandlers)()) return;
|
|
387
|
+
for (const [kind, handler] of Object.entries(cacheHandlers)){
|
|
388
|
+
if (!handler) continue;
|
|
389
|
+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async getIncrementalCache({ requestHeaders, requestProtocol }) {
|
|
393
|
+
const dev = !!this.renderOpts.dev;
|
|
394
|
+
let CacheHandler;
|
|
395
|
+
const { cacheHandler } = this.nextConfig;
|
|
396
|
+
if (cacheHandler) {
|
|
397
|
+
CacheHandler = (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, cacheHandler)));
|
|
398
|
+
}
|
|
399
|
+
await this.loadCustomCacheHandlers();
|
|
400
|
+
// incremental-cache is request specific
|
|
401
|
+
// although can have shared caches in module scope
|
|
402
|
+
// per-cache handler
|
|
403
|
+
return new _incrementalcache.IncrementalCache({
|
|
404
|
+
fs: this.getCacheFilesystem(),
|
|
405
|
+
dev,
|
|
406
|
+
requestHeaders,
|
|
407
|
+
requestProtocol,
|
|
408
|
+
allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys,
|
|
409
|
+
minimalMode: this.minimalMode,
|
|
410
|
+
serverDistDir: this.serverDistDir,
|
|
411
|
+
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
|
|
412
|
+
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
|
|
413
|
+
flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk,
|
|
414
|
+
getPrerenderManifest: ()=>this.getPrerenderManifest(),
|
|
415
|
+
CurCacheHandler: CacheHandler
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
getEnabledDirectories(dev) {
|
|
419
|
+
const dir = dev ? this.dir : this.serverDistDir;
|
|
420
|
+
return {
|
|
421
|
+
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
422
|
+
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
// ...
|
|
426
|
+
}"
|
|
427
|
+
`);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removed unused `require.resolve` calls in Open Next.
|
|
3
|
+
*/
|
|
4
|
+
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
5
|
+
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
6
|
+
export declare function patchResolveCache(updater: ContentUpdater, buildOpts: BuildOptions): Plugin;
|
|
7
|
+
export declare const cacheHandlerRule = "\nrule:\n pattern: var cacheHandlerPath = __require.resolve(\"./cache.cjs\");\nfix: |-\n var cacheHandlerPath = \"\";\n";
|
|
8
|
+
export declare const compositeCacheHandlerRule = "\nrule:\n pattern: var composableCacheHandlerPath = __require.resolve(\"./composable-cache.cjs\");\nfix: |-\n var composableCacheHandlerPath = \"\";\n";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removed unused `require.resolve` calls in Open Next.
|
|
3
|
+
*/
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
6
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
7
|
+
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
8
|
+
export function patchResolveCache(updater, buildOpts) {
|
|
9
|
+
const { outputDir } = buildOpts;
|
|
10
|
+
const packagePath = getPackagePath(buildOpts);
|
|
11
|
+
const outputPath = path.join(outputDir, "server-functions/default");
|
|
12
|
+
const indexPath = path.relative(buildOpts.appBuildOutputPath, path.join(outputPath, packagePath, `index.mjs`));
|
|
13
|
+
return updater.updateContent("patch-resolve-cache", [
|
|
14
|
+
{
|
|
15
|
+
field: {
|
|
16
|
+
filter: getCrossPlatformPathRegex(indexPath),
|
|
17
|
+
contentFilter: /cacheHandlerPath/,
|
|
18
|
+
callback: async ({ contents }) => {
|
|
19
|
+
contents = patchCode(contents, cacheHandlerRule);
|
|
20
|
+
contents = patchCode(contents, compositeCacheHandlerRule);
|
|
21
|
+
return contents;
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
export const cacheHandlerRule = `
|
|
28
|
+
rule:
|
|
29
|
+
pattern: var cacheHandlerPath = __require.resolve("./cache.cjs");
|
|
30
|
+
fix: |-
|
|
31
|
+
var cacheHandlerPath = "";
|
|
32
|
+
`;
|
|
33
|
+
export const compositeCacheHandlerRule = `
|
|
34
|
+
rule:
|
|
35
|
+
pattern: var composableCacheHandlerPath = __require.resolve("./composable-cache.cjs");
|
|
36
|
+
fix: |-
|
|
37
|
+
var composableCacheHandlerPath = "";
|
|
38
|
+
`;
|
|
@@ -108,5 +108,10 @@ function populateProcessEnv(url, env) {
|
|
|
108
108
|
port: url.port,
|
|
109
109
|
},
|
|
110
110
|
});
|
|
111
|
+
/* We need to set this environment variable to make redirects work properly in preview mode.
|
|
112
|
+
* Next sets this in standalone mode during `startServer`. Without this the protocol would always be `https` here:
|
|
113
|
+
* https://github.com/vercel/next.js/blob/6b1e48080e896e0d44a05fe009cb79d2d3f91774/packages/next/src/server/app-render/action-handler.ts#L307-L316
|
|
114
|
+
*/
|
|
115
|
+
process.env.__NEXT_PRIVATE_ORIGIN = url.origin;
|
|
111
116
|
}
|
|
112
117
|
/* eslint-enable no-var */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennextjs/cloudflare",
|
|
3
3
|
"description": "Cloudflare builder for next apps",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opennextjs-cloudflare": "dist/cli/index.js"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@dotenvx/dotenvx": "1.31.0",
|
|
46
|
-
"@opennextjs/aws": "3.6.
|
|
46
|
+
"@opennextjs/aws": "3.6.5",
|
|
47
47
|
"enquirer": "^2.4.1",
|
|
48
48
|
"glob": "^11.0.0",
|
|
49
49
|
"ts-tqdm": "^0.8.6"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^2.1.1"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"wrangler": "^4.
|
|
71
|
+
"wrangler": "^4.19.1"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"clean": "rimraf dist",
|
|
@@ -1,14 +0,0 @@
|
|
|
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, buildOpts: BuildOptions): Promise<string>;
|
|
14
|
-
export declare function patchComposableCache(code: string, buildOpts: BuildOptions): Promise<string>;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
3
|
-
import { normalizePath } from "../../utils/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* Sets up the OpenNext cache handler in a Next.js build.
|
|
6
|
-
*
|
|
7
|
-
* The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
|
|
8
|
-
* Next.js would then do a dynamic require on a transformed version of the path to retrieve the
|
|
9
|
-
* cache handler and create a new instance of it.
|
|
10
|
-
*
|
|
11
|
-
* This is problematic in workerd due to the dynamic import of the file that is not known from
|
|
12
|
-
* build-time. Therefore, we have to manually override the default way that the cache handler is
|
|
13
|
-
* instantiated with a dynamic require that uses a string literal for the path.
|
|
14
|
-
*/
|
|
15
|
-
export async function patchCache(code, buildOpts) {
|
|
16
|
-
const { outputDir } = buildOpts;
|
|
17
|
-
// TODO: switch to cache.mjs
|
|
18
|
-
const outputPath = path.join(outputDir, "server-functions/default");
|
|
19
|
-
const cacheFile = path.join(outputPath, getPackagePath(buildOpts), "cache.cjs");
|
|
20
|
-
return code.replace("const { cacheHandler } = this.nextConfig;", `
|
|
21
|
-
const cacheHandler = null;
|
|
22
|
-
CacheHandler = require('${normalizePath(cacheFile)}').default;
|
|
23
|
-
`);
|
|
24
|
-
}
|
|
25
|
-
export async function patchComposableCache(code, buildOpts) {
|
|
26
|
-
const { outputDir } = buildOpts;
|
|
27
|
-
// TODO: switch to mjs
|
|
28
|
-
const outputPath = path.join(outputDir, "server-functions/default");
|
|
29
|
-
const cacheFile = path.join(outputPath, getPackagePath(buildOpts), "composable-cache.cjs");
|
|
30
|
-
//TODO: Do we want to move this to the new CodePatcher ?
|
|
31
|
-
return code.replace("const { cacheHandlers } = this.nextConfig.experimental", `
|
|
32
|
-
const cacheHandlers = null;
|
|
33
|
-
const handlersSymbol = Symbol.for('@next/cache-handlers');
|
|
34
|
-
const handlersMapSymbol = Symbol.for('@next/cache-handlers-map');
|
|
35
|
-
const handlersSetSymbol = Symbol.for('@next/cache-handlers-set');
|
|
36
|
-
globalThis[handlersMapSymbol] = new Map();
|
|
37
|
-
globalThis[handlersMapSymbol].set("default", require('${normalizePath(cacheFile)}').default);
|
|
38
|
-
globalThis[handlersSetSymbol] = new Set(globalThis[handlersMapSymbol].values());
|
|
39
|
-
`);
|
|
40
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
|
-
*/
|
|
4
|
-
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
5
|
-
export declare function inlineBuildId(updater: ContentUpdater): Plugin;
|
|
6
|
-
export declare const rule = "\nrule:\n kind: method_definition\n has:\n field: name\n regex: ^getBuildId$\nfix: |-\n getBuildId() {\n return process.env.NEXT_BUILD_ID;\n }\n";
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
|
-
*/
|
|
4
|
-
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
5
|
-
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
6
|
-
export function inlineBuildId(updater) {
|
|
7
|
-
return updater.updateContent("inline-build-id", [
|
|
8
|
-
{
|
|
9
|
-
field: {
|
|
10
|
-
filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/next-server\.js$`, {
|
|
11
|
-
escape: false,
|
|
12
|
-
}),
|
|
13
|
-
contentFilter: /getBuildId\(/,
|
|
14
|
-
callback: ({ contents }) => patchCode(contents, rule),
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
]);
|
|
18
|
-
}
|
|
19
|
-
export const rule = `
|
|
20
|
-
rule:
|
|
21
|
-
kind: method_definition
|
|
22
|
-
has:
|
|
23
|
-
field: name
|
|
24
|
-
regex: ^getBuildId$
|
|
25
|
-
fix: |-
|
|
26
|
-
getBuildId() {
|
|
27
|
-
return process.env.NEXT_BUILD_ID;
|
|
28
|
-
}
|
|
29
|
-
`;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
|
-
import { rule } from "./build-id.js";
|
|
4
|
-
describe("getBuildId", () => {
|
|
5
|
-
test("patch", () => {
|
|
6
|
-
const code = `
|
|
7
|
-
class NextNodeServer extends _baseserver.default {
|
|
8
|
-
constructor(options){
|
|
9
|
-
// Initialize super class
|
|
10
|
-
super(options);
|
|
11
|
-
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
12
|
-
}
|
|
13
|
-
async handleUpgrade() {
|
|
14
|
-
// The web server does not support web sockets, it's only used for HMR in
|
|
15
|
-
// development.
|
|
16
|
-
}
|
|
17
|
-
loadEnvConfig({ dev, forceReload, silent }) {
|
|
18
|
-
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
19
|
-
info: ()=>{},
|
|
20
|
-
error: ()=>{}
|
|
21
|
-
} : _log, forceReload);
|
|
22
|
-
}
|
|
23
|
-
async hasPage(pathname) {
|
|
24
|
-
var _this_nextConfig_i18n;
|
|
25
|
-
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
26
|
-
}
|
|
27
|
-
getBuildId() {
|
|
28
|
-
const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE);
|
|
29
|
-
try {
|
|
30
|
-
return _fs.default.readFileSync(buildIdFile, "utf8").trim();
|
|
31
|
-
} catch (err) {
|
|
32
|
-
if (err.code === "ENOENT") {
|
|
33
|
-
throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`);
|
|
34
|
-
}
|
|
35
|
-
throw err;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
getEnabledDirectories(dev) {
|
|
39
|
-
const dir = dev ? this.dir : this.serverDistDir;
|
|
40
|
-
return {
|
|
41
|
-
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
42
|
-
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
// ...
|
|
46
|
-
}`;
|
|
47
|
-
expect(patchCode(code, rule)).toMatchInlineSnapshot(`
|
|
48
|
-
"class NextNodeServer extends _baseserver.default {
|
|
49
|
-
constructor(options){
|
|
50
|
-
// Initialize super class
|
|
51
|
-
super(options);
|
|
52
|
-
this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ };
|
|
53
|
-
}
|
|
54
|
-
async handleUpgrade() {
|
|
55
|
-
// The web server does not support web sockets, it's only used for HMR in
|
|
56
|
-
// development.
|
|
57
|
-
}
|
|
58
|
-
loadEnvConfig({ dev, forceReload, silent }) {
|
|
59
|
-
(0, _env.loadEnvConfig)(this.dir, dev, silent ? {
|
|
60
|
-
info: ()=>{},
|
|
61
|
-
error: ()=>{}
|
|
62
|
-
} : _log, forceReload);
|
|
63
|
-
}
|
|
64
|
-
async hasPage(pathname) {
|
|
65
|
-
var _this_nextConfig_i18n;
|
|
66
|
-
return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app);
|
|
67
|
-
}
|
|
68
|
-
getBuildId() {
|
|
69
|
-
return process.env.NEXT_BUILD_ID;
|
|
70
|
-
}
|
|
71
|
-
getEnabledDirectories(dev) {
|
|
72
|
-
const dir = dev ? this.dir : this.serverDistDir;
|
|
73
|
-
return {
|
|
74
|
-
app: (0, _findpagesdir.findDir)(dir, "app") ? true : false,
|
|
75
|
-
pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
// ...
|
|
79
|
-
}"
|
|
80
|
-
`);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline `evalManifest` as it relies on `readFileSync` and `runInNewContext`
|
|
3
|
-
* that are not supported by workerd.
|
|
4
|
-
*/
|
|
5
|
-
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
6
|
-
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
7
|
-
export declare function inlineEvalManifest(updater: ContentUpdater, buildOpts: BuildOptions): Plugin;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline `evalManifest` as it relies on `readFileSync` and `runInNewContext`
|
|
3
|
-
* that are not supported by workerd.
|
|
4
|
-
*/
|
|
5
|
-
import { join, posix, relative, sep } from "node:path";
|
|
6
|
-
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
7
|
-
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
8
|
-
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
9
|
-
import { glob } from "glob";
|
|
10
|
-
import { normalizePath } from "../../utils/normalize-path.js";
|
|
11
|
-
export function inlineEvalManifest(updater, buildOpts) {
|
|
12
|
-
return updater.updateContent("inline-eval-manifest", [
|
|
13
|
-
{
|
|
14
|
-
field: {
|
|
15
|
-
filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/load-manifest(\.external)?\.js$`, {
|
|
16
|
-
escape: false,
|
|
17
|
-
}),
|
|
18
|
-
contentFilter: /function evalManifest\(/,
|
|
19
|
-
callback: async ({ contents }) => patchCode(contents, await getRule(buildOpts)),
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
]);
|
|
23
|
-
}
|
|
24
|
-
async function getRule(buildOpts) {
|
|
25
|
-
const { outputDir } = buildOpts;
|
|
26
|
-
const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
|
|
27
|
-
const appDir = join(baseDir, "server/app");
|
|
28
|
-
const manifests = await glob(join(baseDir, "**/*_client-reference-manifest.js"), {
|
|
29
|
-
windowsPathsNoEscape: true,
|
|
30
|
-
});
|
|
31
|
-
const returnManifests = manifests
|
|
32
|
-
.map((manifest) => {
|
|
33
|
-
const endsWith = normalizePath(relative(baseDir, manifest));
|
|
34
|
-
const key = normalizePath("/" + relative(appDir, manifest)).replace("_client-reference-manifest.js", "");
|
|
35
|
-
return `
|
|
36
|
-
if ($PATH.endsWith("${endsWith}")) {
|
|
37
|
-
require(${JSON.stringify(manifest)});
|
|
38
|
-
return {
|
|
39
|
-
__RSC_MANIFEST: {
|
|
40
|
-
"${key}": globalThis.__RSC_MANIFEST["${key}"],
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
`;
|
|
45
|
-
})
|
|
46
|
-
.join("\n");
|
|
47
|
-
return {
|
|
48
|
-
rule: {
|
|
49
|
-
pattern: `
|
|
50
|
-
function evalManifest($PATH, $$$ARGS) {
|
|
51
|
-
$$$_
|
|
52
|
-
}`,
|
|
53
|
-
},
|
|
54
|
-
fix: `
|
|
55
|
-
function evalManifest($PATH, $$$ARGS) {
|
|
56
|
-
$PATH = $PATH.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)});
|
|
57
|
-
${returnManifests}
|
|
58
|
-
throw new Error(\`Unexpected evalManifest(\${$PATH}) call!\`);
|
|
59
|
-
}`,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type BuildOptions } from "@opennextjs/aws/build/helper.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, buildOpts: BuildOptions): string;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
4
|
-
/**
|
|
5
|
-
* Inlines the middleware manifest from the build output to prevent a dynamic require statement
|
|
6
|
-
* as they result in runtime failures.
|
|
7
|
-
*/
|
|
8
|
-
export function inlineMiddlewareManifestRequire(code, buildOpts) {
|
|
9
|
-
const { outputDir } = buildOpts;
|
|
10
|
-
const middlewareManifestPath = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server/middleware-manifest.json");
|
|
11
|
-
const middlewareManifest = existsSync(middlewareManifestPath)
|
|
12
|
-
? JSON.parse(readFileSync(middlewareManifestPath, "utf-8"))
|
|
13
|
-
: {};
|
|
14
|
-
return code.replace("require(this.middlewareManifestPath)", JSON.stringify(middlewareManifest));
|
|
15
|
-
}
|
|
File without changes
|