@opennextjs/cloudflare 1.0.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/cloudflare-context.d.ts +5 -0
- package/dist/api/config.d.ts +12 -2
- package/dist/api/config.js +9 -2
- package/dist/api/durable-objects/bucket-cache-purge.d.ts +7 -0
- package/dist/api/durable-objects/bucket-cache-purge.js +75 -0
- package/dist/api/durable-objects/bucket-cache-purge.spec.js +121 -0
- package/dist/api/overrides/cache-purge/index.d.ts +12 -0
- package/dist/api/overrides/cache-purge/index.js +26 -0
- package/dist/api/overrides/internal.d.ts +2 -0
- package/dist/api/overrides/internal.js +52 -0
- package/dist/api/overrides/queue/do-queue.js +1 -1
- package/dist/api/overrides/queue/queue-cache.d.ts +36 -0
- package/dist/api/overrides/queue/queue-cache.js +93 -0
- package/dist/api/overrides/queue/queue-cache.spec.d.ts +1 -0
- package/dist/api/overrides/queue/queue-cache.spec.js +92 -0
- package/dist/api/overrides/tag-cache/d1-next-tag-cache.js +2 -1
- package/dist/api/overrides/tag-cache/do-sharded-tag-cache.d.ts +20 -0
- package/dist/api/overrides/tag-cache/do-sharded-tag-cache.js +70 -7
- package/dist/api/overrides/tag-cache/do-sharded-tag-cache.spec.js +81 -1
- package/dist/cli/build/bundle-server.d.ts +1 -1
- package/dist/cli/build/bundle-server.js +16 -38
- package/dist/cli/build/open-next/compileDurableObjects.js +1 -0
- 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.d.ts +1 -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 +39 -0
- 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/dist/cli/templates/worker.d.ts +1 -0
- package/dist/cli/templates/worker.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 → api/durable-objects/bucket-cache-purge.spec.d.ts} +0 -0
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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,39 @@
|
|
|
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
|
+
console.error({ index: indexPath });
|
|
14
|
+
return updater.updateContent("patch-resolve-cache", [
|
|
15
|
+
{
|
|
16
|
+
field: {
|
|
17
|
+
filter: getCrossPlatformPathRegex(indexPath),
|
|
18
|
+
contentFilter: /cacheHandlerPath/,
|
|
19
|
+
callback: async ({ contents }) => {
|
|
20
|
+
contents = patchCode(contents, cacheHandlerRule);
|
|
21
|
+
contents = patchCode(contents, compositeCacheHandlerRule);
|
|
22
|
+
return contents;
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
export const cacheHandlerRule = `
|
|
29
|
+
rule:
|
|
30
|
+
pattern: var cacheHandlerPath = __require.resolve("./cache.cjs");
|
|
31
|
+
fix: |-
|
|
32
|
+
var cacheHandlerPath = "";
|
|
33
|
+
`;
|
|
34
|
+
export const compositeCacheHandlerRule = `
|
|
35
|
+
rule:
|
|
36
|
+
pattern: var composableCacheHandlerPath = __require.resolve("./composable-cache.cjs");
|
|
37
|
+
fix: |-
|
|
38
|
+
var composableCacheHandlerPath = "";
|
|
39
|
+
`;
|
|
@@ -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 */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { DOQueueHandler } from "./.build/durable-objects/queue.js";
|
|
2
2
|
export { DOShardedTagCache } from "./.build/durable-objects/sharded-tag-cache.js";
|
|
3
|
+
export { BucketCachePurge } from "./.build/durable-objects/bucket-cache-purge.js";
|
|
3
4
|
declare const _default: {
|
|
4
5
|
fetch(request: Request<unknown, IncomingRequestCfProperties<unknown>>, env: CloudflareEnv, ctx: ExecutionContext): Promise<any>;
|
|
5
6
|
};
|
|
@@ -6,6 +6,8 @@ import { handler as middlewareHandler } from "./middleware/handler.mjs";
|
|
|
6
6
|
export { DOQueueHandler } from "./.build/durable-objects/queue.js";
|
|
7
7
|
//@ts-expect-error: Will be resolved by wrangler build
|
|
8
8
|
export { DOShardedTagCache } from "./.build/durable-objects/sharded-tag-cache.js";
|
|
9
|
+
//@ts-expect-error: Will be resolved by wrangler build
|
|
10
|
+
export { BucketCachePurge } from "./.build/durable-objects/bucket-cache-purge.js";
|
|
9
11
|
export default {
|
|
10
12
|
async fetch(request, env, ctx) {
|
|
11
13
|
return runWithCloudflareRequestContext(request, env, ctx, async () => {
|
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.0
|
|
4
|
+
"version": "1.2.0",
|
|
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": "
|
|
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>;
|