@opennextjs/cloudflare 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/cloudflare-context.d.ts +3 -0
- package/dist/api/cloudflare-context.js +5 -5
- package/dist/api/d1-tag-cache.d.ts +35 -0
- package/dist/api/d1-tag-cache.js +147 -0
- package/dist/cli/build/build.js +5 -1
- package/dist/cli/build/bundle-server.js +6 -4
- package/dist/cli/build/open-next/compile-cache-assets-manifest.d.ts +6 -0
- package/dist/cli/build/open-next/compile-cache-assets-manifest.js +17 -0
- package/dist/cli/build/patches/index.d.ts +1 -1
- package/dist/cli/build/patches/index.js +1 -1
- package/dist/cli/build/patches/plugins/build-id.d.ts +6 -0
- package/dist/cli/build/patches/plugins/build-id.js +22 -0
- package/dist/cli/build/patches/plugins/build-id.spec.js +82 -0
- package/dist/cli/build/patches/plugins/find-dir.d.ts +1 -2
- package/dist/cli/build/patches/plugins/find-dir.js +1 -2
- package/dist/cli/build/patches/plugins/instrumentation.d.ts +8 -0
- package/dist/cli/build/patches/plugins/instrumentation.js +62 -0
- package/dist/cli/build/patches/plugins/instrumentation.spec.d.ts +1 -0
- package/dist/cli/build/patches/plugins/instrumentation.spec.js +91 -0
- package/dist/cli/build/patches/plugins/load-manifest.d.ts +1 -1
- package/dist/cli/build/patches/plugins/load-manifest.js +1 -1
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.d.ts +9 -0
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.js +36 -0
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.spec.d.ts +1 -0
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.spec.js +29 -0
- package/dist/cli/build/utils/ensure-cf-config.js +2 -1
- package/package.json +2 -2
- package/dist/cli/build/patches/plugins/load-instrumentation.d.ts +0 -6
- package/dist/cli/build/patches/plugins/load-instrumentation.js +0 -16
- package/dist/cli/build/patches/plugins/load-instrumentation.spec.js +0 -45
- package/dist/cli/build/patches/to-investigate/index.d.ts +0 -3
- package/dist/cli/build/patches/to-investigate/index.js +0 -3
- package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.d.ts +0 -7
- package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.js +0 -17
- package/dist/cli/build/patches/to-investigate/patch-read-file.d.ts +0 -2
- package/dist/cli/build/patches/to-investigate/patch-read-file.js +0 -7
- /package/dist/cli/build/patches/plugins/{load-instrumentation.spec.d.ts → build-id.spec.d.ts} +0 -0
- /package/dist/cli/build/patches/to-investigate/{inline-middleware-manifest-require.d.ts → inline-middleware-manifest.d.ts} +0 -0
- /package/dist/cli/build/patches/to-investigate/{inline-middleware-manifest-require.js → inline-middleware-manifest.js} +0 -0
|
@@ -38,11 +38,11 @@ function getCloudflareContextSync() {
|
|
|
38
38
|
// can work during SSG since for SSG Next.js creates (jest) workers that don't get access to the
|
|
39
39
|
// normal global state so we throw with a helpful error message.
|
|
40
40
|
if (inSSG()) {
|
|
41
|
-
throw new Error(`\n\nERROR: \`getCloudflareContext\` has been called in a static route,` +
|
|
42
|
-
`
|
|
43
|
-
`
|
|
44
|
-
`
|
|
45
|
-
`
|
|
41
|
+
throw new Error(`\n\nERROR: \`getCloudflareContext\` has been called in sync mode in either a static route or at the top level of a non-static one,` +
|
|
42
|
+
` both cases are not allowed but can be solved by either:\n` +
|
|
43
|
+
` - make sure that the call is not at the top level and that the route is not static\n` +
|
|
44
|
+
` - call \`getCloudflareContext({async: true})\` to use the \`async\` mode\n` +
|
|
45
|
+
` - avoid calling \`getCloudflareContext\` in the route\n`);
|
|
46
46
|
}
|
|
47
47
|
throw new Error(initOpenNextCloudflareForDevErrorMsg);
|
|
48
48
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TagCache } from "@opennextjs/aws/types/overrides.js";
|
|
2
|
+
/**
|
|
3
|
+
* An instance of the Tag Cache that uses a D1 binding (`NEXT_CACHE_D1`) as it's underlying data store.
|
|
4
|
+
*
|
|
5
|
+
* **Tag/path mappings table**
|
|
6
|
+
*
|
|
7
|
+
* Information about the relation between tags and paths is stored in a `tags` table that contains
|
|
8
|
+
* two columns; `tag`, and `path`. The table name can be configured with `NEXT_CACHE_D1_TAGS_TABLE`
|
|
9
|
+
* environment variable.
|
|
10
|
+
*
|
|
11
|
+
* This table should be populated using an SQL file that is generated during the build process.
|
|
12
|
+
*
|
|
13
|
+
* **Tag revalidations table**
|
|
14
|
+
*
|
|
15
|
+
* Revalidation times for tags are stored in a `revalidations` table that contains two columns; `tags`,
|
|
16
|
+
* and `revalidatedAt`. The table name can be configured with `NEXT_CACHE_D1_REVALIDATIONS_TABLE`
|
|
17
|
+
* environment variable.
|
|
18
|
+
*/
|
|
19
|
+
declare class D1TagCache implements TagCache {
|
|
20
|
+
readonly name = "d1-tag-cache";
|
|
21
|
+
getByPath(rawPath: string): Promise<string[]>;
|
|
22
|
+
getByTag(rawTag: string): Promise<string[]>;
|
|
23
|
+
getLastModified(path: string, lastModified?: number): Promise<number>;
|
|
24
|
+
writeTags(tags: {
|
|
25
|
+
tag: string;
|
|
26
|
+
path: string;
|
|
27
|
+
revalidatedAt?: number;
|
|
28
|
+
}[]): Promise<void>;
|
|
29
|
+
private getConfig;
|
|
30
|
+
protected removeBuildId(key: string): string;
|
|
31
|
+
protected getCacheKey(key: string): string;
|
|
32
|
+
protected getBuildId(): string;
|
|
33
|
+
}
|
|
34
|
+
declare const _default: D1TagCache;
|
|
35
|
+
export default _default;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { debug, error } from "@opennextjs/aws/adapters/logger.js";
|
|
2
|
+
import { RecoverableError } from "@opennextjs/aws/utils/error.js";
|
|
3
|
+
import { getCloudflareContext } from "./cloudflare-context.js";
|
|
4
|
+
/**
|
|
5
|
+
* An instance of the Tag Cache that uses a D1 binding (`NEXT_CACHE_D1`) as it's underlying data store.
|
|
6
|
+
*
|
|
7
|
+
* **Tag/path mappings table**
|
|
8
|
+
*
|
|
9
|
+
* Information about the relation between tags and paths is stored in a `tags` table that contains
|
|
10
|
+
* two columns; `tag`, and `path`. The table name can be configured with `NEXT_CACHE_D1_TAGS_TABLE`
|
|
11
|
+
* environment variable.
|
|
12
|
+
*
|
|
13
|
+
* This table should be populated using an SQL file that is generated during the build process.
|
|
14
|
+
*
|
|
15
|
+
* **Tag revalidations table**
|
|
16
|
+
*
|
|
17
|
+
* Revalidation times for tags are stored in a `revalidations` table that contains two columns; `tags`,
|
|
18
|
+
* and `revalidatedAt`. The table name can be configured with `NEXT_CACHE_D1_REVALIDATIONS_TABLE`
|
|
19
|
+
* environment variable.
|
|
20
|
+
*/
|
|
21
|
+
class D1TagCache {
|
|
22
|
+
name = "d1-tag-cache";
|
|
23
|
+
async getByPath(rawPath) {
|
|
24
|
+
const { isDisabled, db, tables } = this.getConfig();
|
|
25
|
+
if (isDisabled)
|
|
26
|
+
return [];
|
|
27
|
+
const path = this.getCacheKey(rawPath);
|
|
28
|
+
try {
|
|
29
|
+
const { success, results } = await db
|
|
30
|
+
.prepare(`SELECT tag FROM ${JSON.stringify(tables.tags)} WHERE path = ?`)
|
|
31
|
+
.bind(path)
|
|
32
|
+
.all();
|
|
33
|
+
if (!success)
|
|
34
|
+
throw new RecoverableError(`D1 select failed for ${path}`);
|
|
35
|
+
const tags = results?.map((item) => this.removeBuildId(item.tag));
|
|
36
|
+
debug("tags for path", path, tags);
|
|
37
|
+
return tags;
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
error("Failed to get tags by path", e);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async getByTag(rawTag) {
|
|
45
|
+
const { isDisabled, db, tables } = this.getConfig();
|
|
46
|
+
if (isDisabled)
|
|
47
|
+
return [];
|
|
48
|
+
const tag = this.getCacheKey(rawTag);
|
|
49
|
+
try {
|
|
50
|
+
const { success, results } = await db
|
|
51
|
+
.prepare(`SELECT path FROM ${JSON.stringify(tables.tags)} WHERE tag = ?`)
|
|
52
|
+
.bind(tag)
|
|
53
|
+
.all();
|
|
54
|
+
if (!success)
|
|
55
|
+
throw new RecoverableError(`D1 select failed for ${tag}`);
|
|
56
|
+
const paths = results?.map((item) => this.removeBuildId(item.path));
|
|
57
|
+
debug("paths for tag", tag, paths);
|
|
58
|
+
return paths;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
error("Failed to get by tag", e);
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async getLastModified(path, lastModified) {
|
|
66
|
+
const { isDisabled, db, tables } = this.getConfig();
|
|
67
|
+
if (isDisabled)
|
|
68
|
+
return lastModified ?? Date.now();
|
|
69
|
+
try {
|
|
70
|
+
const { success, results } = await db
|
|
71
|
+
.prepare(`SELECT ${JSON.stringify(tables.revalidations)}.tag FROM ${JSON.stringify(tables.revalidations)}
|
|
72
|
+
INNER JOIN ${JSON.stringify(tables.tags)} ON ${JSON.stringify(tables.revalidations)}.tag = ${JSON.stringify(tables.tags)}.tag
|
|
73
|
+
WHERE ${JSON.stringify(tables.tags)}.path = ? AND ${JSON.stringify(tables.revalidations)}.revalidatedAt > ?;`)
|
|
74
|
+
.bind(this.getCacheKey(path), lastModified ?? 0)
|
|
75
|
+
.all();
|
|
76
|
+
if (!success)
|
|
77
|
+
throw new RecoverableError(`D1 select failed for ${path} - ${lastModified ?? 0}`);
|
|
78
|
+
debug("revalidatedTags", results);
|
|
79
|
+
return results?.length > 0 ? -1 : (lastModified ?? Date.now());
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
error("Failed to get revalidated tags", e);
|
|
83
|
+
return lastModified ?? Date.now();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async writeTags(tags) {
|
|
87
|
+
const { isDisabled, db, tables } = this.getConfig();
|
|
88
|
+
if (isDisabled || tags.length === 0)
|
|
89
|
+
return;
|
|
90
|
+
try {
|
|
91
|
+
const uniqueTags = new Set();
|
|
92
|
+
const results = await db.batch(tags
|
|
93
|
+
.map(({ tag, path, revalidatedAt }) => {
|
|
94
|
+
if (revalidatedAt === 1) {
|
|
95
|
+
// new tag/path mapping from set
|
|
96
|
+
return db
|
|
97
|
+
.prepare(`INSERT INTO ${JSON.stringify(tables.tags)} (tag, path) VALUES (?, ?)`)
|
|
98
|
+
.bind(this.getCacheKey(tag), this.getCacheKey(path));
|
|
99
|
+
}
|
|
100
|
+
if (!uniqueTags.has(tag) && revalidatedAt !== -1) {
|
|
101
|
+
// tag was revalidated
|
|
102
|
+
uniqueTags.add(tag);
|
|
103
|
+
return db
|
|
104
|
+
.prepare(`INSERT INTO ${JSON.stringify(tables.revalidations)} (tag, revalidatedAt) VALUES (?, ?)`)
|
|
105
|
+
.bind(this.getCacheKey(tag), revalidatedAt ?? Date.now());
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
.filter((stmt) => !!stmt));
|
|
109
|
+
const failedResults = results.filter((res) => !res.success);
|
|
110
|
+
if (failedResults.length > 0) {
|
|
111
|
+
throw new RecoverableError(`${failedResults.length} tags failed to write`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
error("Failed to batch write tags", e);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
getConfig() {
|
|
119
|
+
const cfEnv = getCloudflareContext().env;
|
|
120
|
+
const db = cfEnv.NEXT_CACHE_D1;
|
|
121
|
+
if (!db)
|
|
122
|
+
debug("No D1 database found");
|
|
123
|
+
const isDisabled = !!globalThis.openNextConfig
|
|
124
|
+
.dangerous?.disableTagCache;
|
|
125
|
+
if (!db || isDisabled) {
|
|
126
|
+
return { isDisabled: true };
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
isDisabled: false,
|
|
130
|
+
db,
|
|
131
|
+
tables: {
|
|
132
|
+
tags: cfEnv.NEXT_CACHE_D1_TAGS_TABLE ?? "tags",
|
|
133
|
+
revalidations: cfEnv.NEXT_CACHE_D1_REVALIDATIONS_TABLE ?? "revalidations",
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
removeBuildId(key) {
|
|
138
|
+
return key.replace(`${this.getBuildId()}/`, "");
|
|
139
|
+
}
|
|
140
|
+
getCacheKey(key) {
|
|
141
|
+
return `${this.getBuildId()}/${key}`.replaceAll("//", "/");
|
|
142
|
+
}
|
|
143
|
+
getBuildId() {
|
|
144
|
+
return process.env.NEXT_BUILD_ID ?? "no-build-id";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export default new D1TagCache();
|
package/dist/cli/build/build.js
CHANGED
|
@@ -9,6 +9,7 @@ import * as buildHelper from "@opennextjs/aws/build/helper.js";
|
|
|
9
9
|
import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
|
|
10
10
|
import logger from "@opennextjs/aws/logger.js";
|
|
11
11
|
import { bundleServer } from "./bundle-server.js";
|
|
12
|
+
import { compileCacheAssetsManifestSqlFile } from "./open-next/compile-cache-assets-manifest.js";
|
|
12
13
|
import { compileEnvFiles } from "./open-next/compile-env-files.js";
|
|
13
14
|
import { copyCacheAssets } from "./open-next/copyCacheAssets.js";
|
|
14
15
|
import { createServerBundle } from "./open-next/createServerBundle.js";
|
|
@@ -64,8 +65,11 @@ export async function build(projectOpts) {
|
|
|
64
65
|
await createMiddleware(options, { forceOnlyBuildOnce: true });
|
|
65
66
|
createStaticAssets(options);
|
|
66
67
|
if (config.dangerous?.disableIncrementalCache !== true) {
|
|
67
|
-
createCacheAssets(options);
|
|
68
|
+
const { useTagCache, metaFiles } = createCacheAssets(options);
|
|
68
69
|
copyCacheAssets(options);
|
|
70
|
+
if (useTagCache) {
|
|
71
|
+
compileCacheAssetsManifestSqlFile(options, metaFiles);
|
|
72
|
+
}
|
|
69
73
|
}
|
|
70
74
|
await createServerBundle(options);
|
|
71
75
|
await bundleServer(options);
|
|
@@ -7,13 +7,15 @@ import { build } from "esbuild";
|
|
|
7
7
|
import { patchVercelOgLibrary } from "./patches/ast/patch-vercel-og-library.js";
|
|
8
8
|
import { patchWebpackRuntime } from "./patches/ast/webpack-runtime.js";
|
|
9
9
|
import * as patches from "./patches/index.js";
|
|
10
|
+
import { inlineBuildId } from "./patches/plugins/build-id.js";
|
|
10
11
|
import { ContentUpdater } from "./patches/plugins/content-updater.js";
|
|
11
12
|
import { inlineEvalManifest } from "./patches/plugins/eval-manifest.js";
|
|
12
13
|
import { patchFetchCacheSetMissingWaitUntil } from "./patches/plugins/fetch-cache-wait-until.js";
|
|
13
14
|
import { inlineFindDir } from "./patches/plugins/find-dir.js";
|
|
14
|
-
import {
|
|
15
|
+
import { patchInstrumentation } from "./patches/plugins/instrumentation.js";
|
|
15
16
|
import { inlineLoadManifest } from "./patches/plugins/load-manifest.js";
|
|
16
17
|
import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js";
|
|
18
|
+
import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js";
|
|
17
19
|
import { fixRequire } from "./patches/plugins/require.js";
|
|
18
20
|
import { shimRequireHook } from "./patches/plugins/require-hook.js";
|
|
19
21
|
import { inlineRequirePage } from "./patches/plugins/require-page.js";
|
|
@@ -74,11 +76,13 @@ export async function bundleServer(buildOpts) {
|
|
|
74
76
|
setWranglerExternal(),
|
|
75
77
|
fixRequire(updater),
|
|
76
78
|
handleOptionalDependencies(optionalDependencies),
|
|
77
|
-
|
|
79
|
+
patchInstrumentation(updater, buildOpts),
|
|
78
80
|
patchFetchCacheSetMissingWaitUntil(updater),
|
|
79
81
|
inlineEvalManifest(updater, buildOpts),
|
|
80
82
|
inlineFindDir(updater, buildOpts),
|
|
81
83
|
inlineLoadManifest(updater, buildOpts),
|
|
84
|
+
inlineBuildId(updater),
|
|
85
|
+
patchDepdDeprecations(updater),
|
|
82
86
|
// Apply updater updaters, must be the last plugin
|
|
83
87
|
updater.plugin,
|
|
84
88
|
],
|
|
@@ -166,13 +170,11 @@ export async function updateWorkerBundledCode(workerOutputFile, buildOpts) {
|
|
|
166
170
|
const code = await readFile(workerOutputFile, "utf8");
|
|
167
171
|
const patchedCode = await patchCodeWithValidations(code, [
|
|
168
172
|
["require", patches.patchRequire],
|
|
169
|
-
["`buildId` function", (code) => patches.patchBuildId(code, buildOpts)],
|
|
170
173
|
["cacheHandler", (code) => patches.patchCache(code, buildOpts)],
|
|
171
174
|
[
|
|
172
175
|
"'require(this.middlewareManifestPath)'",
|
|
173
176
|
(code) => patches.inlineMiddlewareManifestRequire(code, buildOpts),
|
|
174
177
|
],
|
|
175
|
-
["exception bubbling", patches.patchExceptionBubbling],
|
|
176
178
|
[
|
|
177
179
|
"`patchAsyncStorage` call",
|
|
178
180
|
(code) => code
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
2
|
+
import type { TagCacheMetaFile } from "@opennextjs/aws/types/cache.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generates SQL statements that can be used to initialise the cache assets manifest in an SQL data store.
|
|
5
|
+
*/
|
|
6
|
+
export declare function compileCacheAssetsManifestSqlFile(options: BuildOptions, metaFiles: TagCacheMetaFile[]): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Generates SQL statements that can be used to initialise the cache assets manifest in an SQL data store.
|
|
5
|
+
*/
|
|
6
|
+
export function compileCacheAssetsManifestSqlFile(options, metaFiles) {
|
|
7
|
+
const outputPath = path.join(options.outputDir, "cloudflare/cache-assets-manifest.sql");
|
|
8
|
+
const tagsTable = process.env.NEXT_CACHE_D1_TAGS_TABLE || "tags";
|
|
9
|
+
const revalidationsTable = process.env.NEXT_CACHE_D1_REVALIDATIONS_TABLE || "revalidations";
|
|
10
|
+
mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
11
|
+
writeFileSync(outputPath, `CREATE TABLE IF NOT EXISTS ${JSON.stringify(tagsTable)} (tag TEXT NOT NULL, path TEXT NOT NULL, UNIQUE(tag, path) ON CONFLICT REPLACE);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS ${JSON.stringify(revalidationsTable)} (tag TEXT NOT NULL, revalidatedAt INTEGER NOT NULL, UNIQUE(tag) ON CONFLICT REPLACE);\n`);
|
|
13
|
+
const values = metaFiles.map(({ tag, path }) => `(${JSON.stringify(tag.S)}, ${JSON.stringify(path.S)})`);
|
|
14
|
+
if (values.length) {
|
|
15
|
+
appendFileSync(outputPath, `INSERT INTO ${JSON.stringify(tagsTable)} (tag, path) VALUES ${values.join(", ")};`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./investigated/index.js";
|
|
2
|
-
export * from "./to-investigate/
|
|
2
|
+
export * from "./to-investigate/inline-middleware-manifest.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./investigated/index.js";
|
|
2
|
-
export * from "./to-investigate/
|
|
2
|
+
export * from "./to-investigate/inline-middleware-manifest.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
|
+
*/
|
|
4
|
+
import type { ContentUpdater } from "./content-updater.js";
|
|
5
|
+
export declare function inlineBuildId(updater: ContentUpdater): import("esbuild").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";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
|
+
*/
|
|
4
|
+
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
5
|
+
import { patchCode } from "../ast/util.js";
|
|
6
|
+
export function inlineBuildId(updater) {
|
|
7
|
+
return updater.updateContent("inline-build-id", {
|
|
8
|
+
filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/next-server\.js$`, { escape: false }),
|
|
9
|
+
contentFilter: /getBuildId\(/,
|
|
10
|
+
}, async ({ contents }) => patchCode(contents, rule));
|
|
11
|
+
}
|
|
12
|
+
export const rule = `
|
|
13
|
+
rule:
|
|
14
|
+
kind: method_definition
|
|
15
|
+
has:
|
|
16
|
+
field: name
|
|
17
|
+
regex: ^getBuildId$
|
|
18
|
+
fix: |-
|
|
19
|
+
getBuildId() {
|
|
20
|
+
return process.env.NEXT_BUILD_ID;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { patchCode } from "../ast/util.js";
|
|
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,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `
|
|
3
|
-
* that are not supported by workerd.
|
|
2
|
+
* Inline `findDir` as it relies on `existsSync` which is not supported by workerd.
|
|
4
3
|
*/
|
|
5
4
|
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
6
5
|
import type { ContentUpdater } from "./content-updater.js";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `
|
|
3
|
-
* that are not supported by workerd.
|
|
2
|
+
* Inline `findDir` as it relies on `existsSync` which is not supported by workerd.
|
|
4
3
|
*/
|
|
5
4
|
import { existsSync } from "node:fs";
|
|
6
5
|
import { join } from "node:path";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
2
|
+
import type { ContentUpdater } from "./content-updater.js";
|
|
3
|
+
export declare function patchInstrumentation(updater: ContentUpdater, buildOpts: BuildOptions): {
|
|
4
|
+
name: string;
|
|
5
|
+
setup(): void;
|
|
6
|
+
};
|
|
7
|
+
export declare function getNext15Rule(builtInstrumentationPath: string | null): string;
|
|
8
|
+
export declare function getNext14Rule(builtInstrumentationPath: string | null): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
4
|
+
import { patchCode } from "../ast/util.js";
|
|
5
|
+
export function patchInstrumentation(updater, buildOpts) {
|
|
6
|
+
const builtInstrumentationPath = getBuiltInstrumentationPath(buildOpts);
|
|
7
|
+
updater.updateContent("patch-instrumentation-next15", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /async loadInstrumentationModule\(/ }, async ({ contents }) => patchCode(contents, getNext15Rule(builtInstrumentationPath)));
|
|
8
|
+
updater.updateContent("patch-instrumentation-next14", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /async prepareImpl\(/ }, async ({ contents }) => patchCode(contents, getNext14Rule(builtInstrumentationPath)));
|
|
9
|
+
return {
|
|
10
|
+
name: "patch-instrumentation",
|
|
11
|
+
setup() { },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function getNext15Rule(builtInstrumentationPath) {
|
|
15
|
+
return `
|
|
16
|
+
rule:
|
|
17
|
+
kind: method_definition
|
|
18
|
+
all:
|
|
19
|
+
- has: {field: name, regex: ^loadInstrumentationModule$}
|
|
20
|
+
- has: {pattern: dynamicRequire, stopBy: end}
|
|
21
|
+
|
|
22
|
+
fix:
|
|
23
|
+
async loadInstrumentationModule() {
|
|
24
|
+
this.instrumentation = ${builtInstrumentationPath ? `require('${builtInstrumentationPath}')` : "null"};
|
|
25
|
+
return this.instrumentation;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
export function getNext14Rule(builtInstrumentationPath) {
|
|
30
|
+
return `
|
|
31
|
+
rule:
|
|
32
|
+
kind: method_definition
|
|
33
|
+
any:
|
|
34
|
+
- has: { field: name, regex: ^prepareImpl$, pattern: $NAME }
|
|
35
|
+
all:
|
|
36
|
+
- has: { pattern: dynamicRequire, stopBy: end }
|
|
37
|
+
- has: { pattern: $_.INSTRUMENTATION_HOOK_FILENAME, stopBy: end }
|
|
38
|
+
fix: |-
|
|
39
|
+
async $NAME() {
|
|
40
|
+
await super.prepareImpl();
|
|
41
|
+
const instrumentationHook = ${builtInstrumentationPath ? `require('${builtInstrumentationPath}')` : "{}"};
|
|
42
|
+
await (instrumentationHook.register == null ? void 0 : instrumentationHook.register.call(instrumentationHook));
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Gets the instrumentation.js file that the Next.js build process generates when an
|
|
48
|
+
* instrumentation hook is provided in the app's source
|
|
49
|
+
*
|
|
50
|
+
* @param buildOpts the open-next build options
|
|
51
|
+
* @returns the path to instrumentation.js, or null if it doesn't exist
|
|
52
|
+
*/
|
|
53
|
+
function getBuiltInstrumentationPath(buildOpts) {
|
|
54
|
+
const { outputDir } = buildOpts;
|
|
55
|
+
const maybeBuiltInstrumentationPath = join(outputDir, "server-functions/default", getPackagePath(buildOpts), `.next/server/${INSTRUMENTATION_HOOK_FILENAME}.js`);
|
|
56
|
+
return existsSync(maybeBuiltInstrumentationPath) ? maybeBuiltInstrumentationPath : null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Pattern to detect instrumentation hooks file
|
|
60
|
+
* (taken from Next.js source: https://github.com/vercel/next.js/blob/1d5820563/packages/next/src/lib/constants.ts#L46-L47)
|
|
61
|
+
*/
|
|
62
|
+
const INSTRUMENTATION_HOOK_FILENAME = "instrumentation";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { patchCode } from "../ast/util.js";
|
|
3
|
+
import { getNext14Rule, getNext15Rule } from "./instrumentation.js";
|
|
4
|
+
describe("LoadInstrumentationModule (Next15)", () => {
|
|
5
|
+
const code = `
|
|
6
|
+
export default class NextNodeServer extends BaseServer {
|
|
7
|
+
protected async loadInstrumentationModule() {
|
|
8
|
+
if (!this.serverOptions.dev) {
|
|
9
|
+
try {
|
|
10
|
+
this.instrumentation = await dynamicRequire(
|
|
11
|
+
resolve(
|
|
12
|
+
this.serverOptions.dir || '.',
|
|
13
|
+
this.serverOptions.conf.distDir!,
|
|
14
|
+
'server',
|
|
15
|
+
INSTRUMENTATION_HOOK_FILENAME
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
} catch (err: any) {
|
|
19
|
+
if (err.code !== 'MODULE_NOT_FOUND') {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'An error occurred while loading the instrumentation hook',
|
|
22
|
+
{ cause: err }
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return this.instrumentation
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
test("patch when an instrumentation file is not present", async () => {
|
|
32
|
+
expect(patchCode(code, getNext15Rule(null))).toMatchInlineSnapshot(`
|
|
33
|
+
"export default class NextNodeServer extends BaseServer {
|
|
34
|
+
async loadInstrumentationModule() { this.instrumentation = null; return this.instrumentation; }
|
|
35
|
+
}
|
|
36
|
+
"
|
|
37
|
+
`);
|
|
38
|
+
});
|
|
39
|
+
test("patch when an instrumentation file is present", async () => {
|
|
40
|
+
expect(patchCode(code, getNext15Rule("/_file_exists_/instrumentation.js"))).toMatchInlineSnapshot(`
|
|
41
|
+
"export default class NextNodeServer extends BaseServer {
|
|
42
|
+
async loadInstrumentationModule() { this.instrumentation = require('/_file_exists_/instrumentation.js'); return this.instrumentation; }
|
|
43
|
+
}
|
|
44
|
+
"
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("prepareImpl (Next14)", () => {
|
|
49
|
+
const code = `
|
|
50
|
+
export default class NextNodeServer extends BaseServer {
|
|
51
|
+
async prepareImpl() {
|
|
52
|
+
await super.prepareImpl();
|
|
53
|
+
if (!this.serverOptions.dev && this.nextConfig.experimental.instrumentationHook) {
|
|
54
|
+
try {
|
|
55
|
+
const instrumentationHook = await dynamicRequire((0, _path.resolve)(this.serverOptions.dir || ".", this.serverOptions.conf.distDir, "server", _constants1.INSTRUMENTATION_HOOK_FILENAME));
|
|
56
|
+
await (instrumentationHook.register == null ? void 0 : instrumentationHook.register.call(instrumentationHook));
|
|
57
|
+
} catch (err2) {
|
|
58
|
+
if (err2.code !== "MODULE_NOT_FOUND") {
|
|
59
|
+
err2.message = \`An error occurred while loading instrumentation hook: \${err2.message}\`;
|
|
60
|
+
throw err2;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
test("patch when an instrumentation file is not present", async () => {
|
|
68
|
+
expect(patchCode(code, getNext14Rule(null))).toMatchInlineSnapshot(`
|
|
69
|
+
"export default class NextNodeServer extends BaseServer {
|
|
70
|
+
async prepareImpl() {
|
|
71
|
+
await super.prepareImpl();
|
|
72
|
+
const instrumentationHook = {};
|
|
73
|
+
await (instrumentationHook.register == null ? void 0 : instrumentationHook.register.call(instrumentationHook));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
"
|
|
77
|
+
`);
|
|
78
|
+
});
|
|
79
|
+
test("patch when an instrumentation file is present", async () => {
|
|
80
|
+
expect(patchCode(code, getNext14Rule("/_file_exists_/instrumentation.js"))).toMatchInlineSnapshot(`
|
|
81
|
+
"export default class NextNodeServer extends BaseServer {
|
|
82
|
+
async prepareImpl() {
|
|
83
|
+
await super.prepareImpl();
|
|
84
|
+
const instrumentationHook = require('/_file_exists_/instrumentation.js');
|
|
85
|
+
await (instrumentationHook.register == null ? void 0 : instrumentationHook.register.call(instrumentationHook));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
"
|
|
89
|
+
`);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `loadManifest` as it relies on `readFileSync`that is not supported by workerd.
|
|
2
|
+
* Inline `loadManifest` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
3
|
*/
|
|
4
4
|
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
5
5
|
import type { ContentUpdater } from "./content-updater.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inline `loadManifest` as it relies on `readFileSync`that is not supported by workerd.
|
|
2
|
+
* Inline `loadManifest` as it relies on `readFileSync` that is not supported by workerd.
|
|
3
3
|
*/
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { join, relative } from "node:path";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ContentUpdater } from "./content-updater.js";
|
|
2
|
+
/**
|
|
3
|
+
* Some dependencies of Next.js use depd to deprecate some of their functions, depd uses `eval` to generate
|
|
4
|
+
* a deprecated version of such functions, this causes `eval` warnings in the terminal even if these functions
|
|
5
|
+
* are never called, this function fixes that by patching the depd `wrapfunction` function so that it still
|
|
6
|
+
* retains the same type of behavior but without using `eval`
|
|
7
|
+
*/
|
|
8
|
+
export declare function patchDepdDeprecations(updater: ContentUpdater): import("esbuild").Plugin;
|
|
9
|
+
export declare const rule = "\nrule:\n kind: function_declaration\n pattern: function wrapfunction($FN, $MESSAGE) { $$$ }\n all:\n - has:\n kind: variable_declarator\n stopBy: end\n has:\n field: name\n pattern: deprecatedfn\n - has:\n kind: call_expression\n stopBy: end\n has:\n kind: identifier\n pattern: eval\nfix:\n function wrapfunction($FN, $MESSAGE) {\n if(typeof $FN !== 'function') throw new Error(\"argument fn must be a function\");\n return function deprecated_$FN(...args) {\n console.warn($MESSAGE);\n return $FN(...args);\n }\n }\n";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { patchCode } from "../ast/util.js";
|
|
2
|
+
/**
|
|
3
|
+
* Some dependencies of Next.js use depd to deprecate some of their functions, depd uses `eval` to generate
|
|
4
|
+
* a deprecated version of such functions, this causes `eval` warnings in the terminal even if these functions
|
|
5
|
+
* are never called, this function fixes that by patching the depd `wrapfunction` function so that it still
|
|
6
|
+
* retains the same type of behavior but without using `eval`
|
|
7
|
+
*/
|
|
8
|
+
export function patchDepdDeprecations(updater) {
|
|
9
|
+
return updater.updateContent("patch-depd-deprecations", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /argument fn must be a function/ }, ({ contents }) => patchCode(contents, rule));
|
|
10
|
+
}
|
|
11
|
+
export const rule = `
|
|
12
|
+
rule:
|
|
13
|
+
kind: function_declaration
|
|
14
|
+
pattern: function wrapfunction($FN, $MESSAGE) { $$$ }
|
|
15
|
+
all:
|
|
16
|
+
- has:
|
|
17
|
+
kind: variable_declarator
|
|
18
|
+
stopBy: end
|
|
19
|
+
has:
|
|
20
|
+
field: name
|
|
21
|
+
pattern: deprecatedfn
|
|
22
|
+
- has:
|
|
23
|
+
kind: call_expression
|
|
24
|
+
stopBy: end
|
|
25
|
+
has:
|
|
26
|
+
kind: identifier
|
|
27
|
+
pattern: eval
|
|
28
|
+
fix:
|
|
29
|
+
function wrapfunction($FN, $MESSAGE) {
|
|
30
|
+
if(typeof $FN !== 'function') throw new Error("argument fn must be a function");
|
|
31
|
+
return function deprecated_$FN(...args) {
|
|
32
|
+
console.warn($MESSAGE);
|
|
33
|
+
return $FN(...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { patchCode } from "../ast/util.js";
|
|
3
|
+
import { rule } from "./patch-depd-deprecations.js";
|
|
4
|
+
describe("patchDepdDeprecations", () => {
|
|
5
|
+
test("patch", () => {
|
|
6
|
+
const code = `
|
|
7
|
+
function prepareObjectStackTrace(e,t){
|
|
8
|
+
return t
|
|
9
|
+
}
|
|
10
|
+
function wrapfunction(fn,message){
|
|
11
|
+
if(typeof fn!=="function"){
|
|
12
|
+
throw new TypeError("argument fn must be a function")
|
|
13
|
+
}
|
|
14
|
+
var args=createArgumentsString(fn.length);
|
|
15
|
+
var deprecate=this;
|
|
16
|
+
var stack=getStack();
|
|
17
|
+
var site=callSiteLocation(stack[1]);
|
|
18
|
+
site.name=fn.name;
|
|
19
|
+
var deprecatedfn=eval("(function ("+args+") {\\n"+'"use strict"\\n'+"log.call(deprecate, message, site)\\n"+"return fn.apply(this, arguments)\\n"+"})");
|
|
20
|
+
return deprecatedfn;
|
|
21
|
+
}`;
|
|
22
|
+
expect(patchCode(code, rule)).toMatchInlineSnapshot(`
|
|
23
|
+
"function prepareObjectStackTrace(e,t){
|
|
24
|
+
return t
|
|
25
|
+
}
|
|
26
|
+
function wrapfunction(fn, message) { if(typeof fn !== 'function') throw new Error("argument fn must be a function"); return function deprecated_fn(...args) { console.warn(message); return fn(...args); } }"
|
|
27
|
+
`);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -10,7 +10,8 @@ export function ensureCloudflareConfig(config) {
|
|
|
10
10
|
dftUseEdgeConverter: config.default?.override?.converter === "edge",
|
|
11
11
|
dftMaybeUseCache: config.default?.override?.incrementalCache === "dummy" ||
|
|
12
12
|
typeof config.default?.override?.incrementalCache === "function",
|
|
13
|
-
|
|
13
|
+
dftMaybeUseTagCache: config.default?.override?.tagCache === "dummy" ||
|
|
14
|
+
typeof config.default?.override?.incrementalCache === "function",
|
|
14
15
|
dftMaybeUseQueue: config.default?.override?.queue === "dummy" ||
|
|
15
16
|
config.default?.override?.queue === "direct" ||
|
|
16
17
|
typeof config.default?.override?.queue === "function",
|
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": "0.5.
|
|
4
|
+
"version": "0.5.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opennextjs-cloudflare": "dist/cli/index.js"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@ast-grep/napi": "^0.34.1",
|
|
65
65
|
"@dotenvx/dotenvx": "1.31.0",
|
|
66
|
-
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@
|
|
66
|
+
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@748",
|
|
67
67
|
"enquirer": "^2.4.1",
|
|
68
68
|
"glob": "^11.0.0",
|
|
69
69
|
"yaml": "^2.7.0"
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `loadInstrumentationModule` uses a dynamic require which is not supported.
|
|
3
|
-
*/
|
|
4
|
-
import type { ContentUpdater } from "./content-updater.js";
|
|
5
|
-
export declare const instrumentationRule = "\nrule:\n kind: method_definition\n all:\n - has: {field: name, regex: ^loadInstrumentationModule$}\n - has: {pattern: dynamicRequire, stopBy: end}\n\nfix: async loadInstrumentationModule() { }\n";
|
|
6
|
-
export declare function patchLoadInstrumentation(updater: ContentUpdater): import("esbuild").Plugin;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `loadInstrumentationModule` uses a dynamic require which is not supported.
|
|
3
|
-
*/
|
|
4
|
-
import { patchCode } from "../ast/util.js";
|
|
5
|
-
export const instrumentationRule = `
|
|
6
|
-
rule:
|
|
7
|
-
kind: method_definition
|
|
8
|
-
all:
|
|
9
|
-
- has: {field: name, regex: ^loadInstrumentationModule$}
|
|
10
|
-
- has: {pattern: dynamicRequire, stopBy: end}
|
|
11
|
-
|
|
12
|
-
fix: async loadInstrumentationModule() { }
|
|
13
|
-
`;
|
|
14
|
-
export function patchLoadInstrumentation(updater) {
|
|
15
|
-
return updater.updateContent("patch-load-instrumentation", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /async loadInstrumentationModule\(/ }, ({ contents }) => patchCode(contents, instrumentationRule));
|
|
16
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
2
|
-
import { patchCode } from "../ast/util.js";
|
|
3
|
-
import { instrumentationRule } from "./load-instrumentation.js";
|
|
4
|
-
describe("LoadInstrumentationModule", () => {
|
|
5
|
-
test("patch", () => {
|
|
6
|
-
const code = `
|
|
7
|
-
export default class NextNodeServer extends BaseServer<
|
|
8
|
-
Options,
|
|
9
|
-
NodeNextRequest,
|
|
10
|
-
NodeNextResponse
|
|
11
|
-
> {
|
|
12
|
-
protected async loadInstrumentationModule() {
|
|
13
|
-
if (!this.serverOptions.dev) {
|
|
14
|
-
try {
|
|
15
|
-
this.instrumentation = await dynamicRequire(
|
|
16
|
-
resolve(
|
|
17
|
-
this.serverOptions.dir || '.',
|
|
18
|
-
this.serverOptions.conf.distDir!,
|
|
19
|
-
'server',
|
|
20
|
-
INSTRUMENTATION_HOOK_FILENAME
|
|
21
|
-
)
|
|
22
|
-
)
|
|
23
|
-
} catch (err: any) {
|
|
24
|
-
if (err.code !== 'MODULE_NOT_FOUND') {
|
|
25
|
-
throw new Error(
|
|
26
|
-
'An error occurred while loading the instrumentation hook',
|
|
27
|
-
{ cause: err }
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return this.instrumentation
|
|
33
|
-
}
|
|
34
|
-
}`;
|
|
35
|
-
expect(patchCode(code, instrumentationRule)).toMatchInlineSnapshot(`
|
|
36
|
-
"export default class NextNodeServer extends BaseServer<
|
|
37
|
-
Options,
|
|
38
|
-
NodeNextRequest,
|
|
39
|
-
NodeNextResponse
|
|
40
|
-
> {
|
|
41
|
-
async loadInstrumentationModule() { }
|
|
42
|
-
}"
|
|
43
|
-
`);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* When using SSG and `dynamicParams = false`, Next.js throws a NoFallbackError. This error is
|
|
3
|
-
* bubbled up by default in Node.js servers, however this causes issues in the workerd with
|
|
4
|
-
* the current response handling and streaming implementation we have, and leads to hanging
|
|
5
|
-
* promises.
|
|
6
|
-
*/
|
|
7
|
-
export declare function patchExceptionBubbling(code: string): string;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* When using SSG and `dynamicParams = false`, Next.js throws a NoFallbackError. This error is
|
|
3
|
-
* bubbled up by default in Node.js servers, however this causes issues in the workerd with
|
|
4
|
-
* the current response handling and streaming implementation we have, and leads to hanging
|
|
5
|
-
* promises.
|
|
6
|
-
*/
|
|
7
|
-
export function patchExceptionBubbling(code) {
|
|
8
|
-
// The code before had: `query._nextBubbleNoFallback = '1'`, that has ben refactored to
|
|
9
|
-
// `addRequestMeta(req, 'bubbleNoFallback', true)` in https://github.com/vercel/next.js/pull/74100
|
|
10
|
-
// we need to support both for backward compatibility, that's why we have the following if statement
|
|
11
|
-
if (code.includes("_nextBubbleNoFallback")) {
|
|
12
|
-
return code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
|
|
13
|
-
}
|
|
14
|
-
// The Next.js transpiled code contains something like `(0, _requestmeta.addRequestMeta)(req, "bubbleNoFallback", true);`
|
|
15
|
-
// and we want to update it to `(0, _requestmeta.addRequestMeta)(req, "bubbleNoFallback", false);`
|
|
16
|
-
return code.replace(/\((.*?.addRequestMeta\)\(.*?,\s+"bubbleNoFallback"),\s+true\)/, "($1, false)");
|
|
17
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { getBuildId } from "@opennextjs/aws/build/helper.js";
|
|
2
|
-
export function patchBuildId(code, buildOpts) {
|
|
3
|
-
// The Next code gets the buildId from the filesystem so we hardcode the value at build time.
|
|
4
|
-
return code.replace("getBuildId() {", `getBuildId() {
|
|
5
|
-
return ${JSON.stringify(getBuildId(buildOpts))};
|
|
6
|
-
`);
|
|
7
|
-
}
|
/package/dist/cli/build/patches/plugins/{load-instrumentation.spec.d.ts → build-id.spec.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|