@opennextjs/cloudflare 1.2.1 → 1.3.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/cli/args.d.ts +2 -0
- package/dist/cli/args.js +37 -19
- package/dist/cli/build/build.d.ts +2 -2
- package/dist/cli/build/build.js +3 -0
- package/dist/cli/build/bundle-server.d.ts +1 -1
- package/dist/cli/build/bundle-server.js +4 -7
- package/dist/cli/build/open-next/compile-images.d.ts +5 -0
- package/dist/cli/build/open-next/compile-images.js +29 -0
- package/dist/cli/build/open-next/createServerBundle.js +0 -1
- package/dist/cli/build/patches/plugins/next-server.d.ts +0 -2
- package/dist/cli/build/patches/plugins/next-server.js +0 -19
- package/dist/cli/build/{patches/investigated → utils}/copy-package-cli-files.js +1 -1
- package/dist/cli/build/utils/index.d.ts +1 -1
- package/dist/cli/build/utils/index.js +1 -1
- package/dist/cli/build/utils/test-patch.d.ts +9 -0
- package/dist/cli/build/utils/test-patch.js +14 -0
- package/dist/cli/build/utils/workerd.js +3 -1
- package/dist/cli/templates/images.d.ts +24 -0
- package/dist/cli/templates/images.js +82 -0
- package/dist/cli/templates/worker.js +3 -3
- package/package.json +5 -2
- package/dist/api/durable-objects/bucket-cache-purge.spec.d.ts +0 -1
- package/dist/api/durable-objects/bucket-cache-purge.spec.js +0 -121
- package/dist/api/durable-objects/queue.spec.d.ts +0 -1
- package/dist/api/durable-objects/queue.spec.js +0 -287
- package/dist/api/durable-objects/sharded-tag-cache.spec.d.ts +0 -1
- package/dist/api/durable-objects/sharded-tag-cache.spec.js +0 -37
- package/dist/api/overrides/queue/memory-queue.spec.d.ts +0 -1
- package/dist/api/overrides/queue/memory-queue.spec.js +0 -76
- package/dist/api/overrides/queue/queue-cache.spec.d.ts +0 -1
- package/dist/api/overrides/queue/queue-cache.spec.js +0 -92
- package/dist/api/overrides/tag-cache/do-sharded-tag-cache.spec.d.ts +0 -1
- package/dist/api/overrides/tag-cache/do-sharded-tag-cache.spec.js +0 -413
- package/dist/api/overrides/tag-cache/tag-cache-filter.spec.d.ts +0 -1
- package/dist/api/overrides/tag-cache/tag-cache-filter.spec.js +0 -97
- package/dist/cli/build/patches/ast/patch-vercel-og-library.spec.d.ts +0 -1
- package/dist/cli/build/patches/ast/patch-vercel-og-library.spec.js +0 -50
- package/dist/cli/build/patches/ast/vercel-og.spec.d.ts +0 -1
- package/dist/cli/build/patches/ast/vercel-og.spec.js +0 -22
- package/dist/cli/build/patches/ast/webpack-runtime.spec.d.ts +0 -1
- package/dist/cli/build/patches/ast/webpack-runtime.spec.js +0 -102
- 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 -2
- package/dist/cli/build/patches/investigated/index.js +0 -2
- package/dist/cli/build/patches/investigated/patch-require.d.ts +0 -4
- package/dist/cli/build/patches/investigated/patch-require.js +0 -6
- package/dist/cli/build/patches/plugins/instrumentation.spec.d.ts +0 -1
- package/dist/cli/build/patches/plugins/instrumentation.spec.js +0 -91
- package/dist/cli/build/patches/plugins/next-server.spec.d.ts +0 -1
- package/dist/cli/build/patches/plugins/next-server.spec.js +0 -429
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.spec.d.ts +0 -1
- package/dist/cli/build/patches/plugins/patch-depd-deprecations.spec.js +0 -29
- package/dist/cli/build/patches/plugins/res-revalidate.spec.d.ts +0 -1
- package/dist/cli/build/patches/plugins/res-revalidate.spec.js +0 -141
- package/dist/cli/build/patches/plugins/use-cache.spec.d.ts +0 -1
- package/dist/cli/build/patches/plugins/use-cache.spec.js +0 -156
- package/dist/cli/build/utils/apply-patches.d.ts +0 -12
- package/dist/cli/build/utils/apply-patches.js +0 -22
- package/dist/cli/build/utils/extract-project-env-vars.spec.d.ts +0 -1
- package/dist/cli/build/utils/extract-project-env-vars.spec.js +0 -67
- package/dist/cli/build/utils/workerd.spec.d.ts +0 -1
- package/dist/cli/build/utils/workerd.spec.js +0 -188
- package/dist/cli/commands/populate-cache.spec.d.ts +0 -1
- package/dist/cli/commands/populate-cache.spec.js +0 -61
- /package/dist/cli/build/{patches/investigated → utils}/copy-package-cli-files.d.ts +0 -0
package/dist/cli/args.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ParseArgsConfig } from "node:util";
|
|
1
2
|
import type { WranglerTarget } from "./utils/run-wrangler.js";
|
|
2
3
|
export type Arguments = ({
|
|
3
4
|
command: "build";
|
|
@@ -17,3 +18,4 @@ export type Arguments = ({
|
|
|
17
18
|
outputDir?: string;
|
|
18
19
|
};
|
|
19
20
|
export declare function getArgs(): Arguments;
|
|
21
|
+
export declare function getPassthroughArgs<T extends ParseArgsConfig>(args: string[], { options }: T): string[];
|
package/dist/cli/args.js
CHANGED
|
@@ -2,28 +2,30 @@ import { mkdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { getWranglerEnvironmentFlag, isWranglerTarget } from "./utils/run-wrangler.js";
|
|
5
|
+
// Config for parsing CLI arguments
|
|
6
|
+
const config = {
|
|
7
|
+
allowPositionals: true,
|
|
8
|
+
strict: false,
|
|
9
|
+
options: {
|
|
10
|
+
skipBuild: { type: "boolean", short: "s", default: false },
|
|
11
|
+
output: { type: "string", short: "o" },
|
|
12
|
+
noMinify: { type: "boolean", default: false },
|
|
13
|
+
skipWranglerConfigCheck: { type: "boolean", default: false },
|
|
14
|
+
cacheChunkSize: { type: "string" },
|
|
15
|
+
},
|
|
16
|
+
};
|
|
5
17
|
export function getArgs() {
|
|
6
|
-
const { positionals, values } = parseArgs(
|
|
7
|
-
|
|
8
|
-
skipBuild: { type: "boolean", short: "s", default: false },
|
|
9
|
-
output: { type: "string", short: "o" },
|
|
10
|
-
noMinify: { type: "boolean", default: false },
|
|
11
|
-
skipWranglerConfigCheck: { type: "boolean", default: false },
|
|
12
|
-
cacheChunkSize: { type: "string" },
|
|
13
|
-
},
|
|
14
|
-
allowPositionals: true,
|
|
15
|
-
});
|
|
16
|
-
const outputDir = values.output ? resolve(values.output) : undefined;
|
|
18
|
+
const { positionals, values } = parseArgs(config);
|
|
19
|
+
const outputDir = typeof values.output === "string" ? resolve(values.output) : undefined;
|
|
17
20
|
if (outputDir)
|
|
18
21
|
assertDirArg(outputDir, "output", true);
|
|
19
|
-
const passthroughArgs = getPassthroughArgs();
|
|
20
22
|
switch (positionals[0]) {
|
|
21
23
|
case "build":
|
|
22
24
|
return {
|
|
23
25
|
command: "build",
|
|
24
26
|
outputDir,
|
|
25
|
-
skipNextBuild: values.skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
|
|
26
|
-
skipWranglerConfigCheck: values.skipWranglerConfigCheck ||
|
|
27
|
+
skipNextBuild: !!values.skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
|
|
28
|
+
skipWranglerConfigCheck: !!values.skipWranglerConfigCheck ||
|
|
27
29
|
["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)),
|
|
28
30
|
minify: !values.noMinify,
|
|
29
31
|
};
|
|
@@ -33,7 +35,7 @@ export function getArgs() {
|
|
|
33
35
|
return {
|
|
34
36
|
command: positionals[0],
|
|
35
37
|
outputDir,
|
|
36
|
-
passthroughArgs,
|
|
38
|
+
passthroughArgs: getPassthroughArgs(process.argv, config),
|
|
37
39
|
...(values.cacheChunkSize && { cacheChunkSize: Number(values.cacheChunkSize) }),
|
|
38
40
|
};
|
|
39
41
|
case "populateCache":
|
|
@@ -44,16 +46,32 @@ export function getArgs() {
|
|
|
44
46
|
command: "populateCache",
|
|
45
47
|
outputDir,
|
|
46
48
|
target: positionals[1],
|
|
47
|
-
environment: getWranglerEnvironmentFlag(
|
|
49
|
+
environment: getWranglerEnvironmentFlag(process.argv),
|
|
48
50
|
...(values.cacheChunkSize && { cacheChunkSize: Number(values.cacheChunkSize) }),
|
|
49
51
|
};
|
|
50
52
|
default:
|
|
51
53
|
throw new Error("Error: invalid command, expected 'build' | 'preview' | 'deploy' | 'upload' | 'populateCache'");
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
|
-
function getPassthroughArgs() {
|
|
55
|
-
const
|
|
56
|
-
|
|
56
|
+
export function getPassthroughArgs(args, { options = {} }) {
|
|
57
|
+
const passthroughArgs = [];
|
|
58
|
+
for (let i = 0; i < args.length; i++) {
|
|
59
|
+
if (args[i] === "--") {
|
|
60
|
+
passthroughArgs.push(...args.slice(i + 1));
|
|
61
|
+
return passthroughArgs;
|
|
62
|
+
}
|
|
63
|
+
// look for `--arg(=value)`, `-arg(=value)`
|
|
64
|
+
const [, name] = /^--?(\w[\w-]*)(=.+)?$/.exec(args[i]) ?? [];
|
|
65
|
+
if (name && !(name in options)) {
|
|
66
|
+
passthroughArgs.push(args[i]);
|
|
67
|
+
// Array args can have multiple values
|
|
68
|
+
// ref https://github.com/yargs/yargs-parser/blob/main/README.md#greedy-arrays
|
|
69
|
+
while (i < args.length - 1 && !args[i + 1]?.startsWith("-")) {
|
|
70
|
+
passthroughArgs.push(args[++i]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return passthroughArgs;
|
|
57
75
|
}
|
|
58
76
|
function assertDirArg(path, argName, make) {
|
|
59
77
|
let dirStats;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as buildHelper from "@opennextjs/aws/build/helper.js";
|
|
2
2
|
import { OpenNextConfig } from "../../api/config.js";
|
|
3
3
|
import type { ProjectOptions } from "../project-options.js";
|
|
4
4
|
/**
|
|
@@ -10,4 +10,4 @@ import type { ProjectOptions } from "../project-options.js";
|
|
|
10
10
|
* @param config The OpenNext config
|
|
11
11
|
* @param projectOpts The options for the project
|
|
12
12
|
*/
|
|
13
|
-
export declare function build(options: BuildOptions, config: OpenNextConfig, projectOpts: ProjectOptions): Promise<void>;
|
|
13
|
+
export declare function build(options: buildHelper.BuildOptions, config: OpenNextConfig, projectOpts: ProjectOptions): Promise<void>;
|
package/dist/cli/build/build.js
CHANGED
|
@@ -8,6 +8,7 @@ import logger from "@opennextjs/aws/logger.js";
|
|
|
8
8
|
import { bundleServer } from "./bundle-server.js";
|
|
9
9
|
import { compileCacheAssetsManifestSqlFile } from "./open-next/compile-cache-assets-manifest.js";
|
|
10
10
|
import { compileEnvFiles } from "./open-next/compile-env-files.js";
|
|
11
|
+
import { compileImages } from "./open-next/compile-images.js";
|
|
11
12
|
import { compileInit } from "./open-next/compile-init.js";
|
|
12
13
|
import { compileDurableObjects } from "./open-next/compileDurableObjects.js";
|
|
13
14
|
import { createServerBundle } from "./open-next/createServerBundle.js";
|
|
@@ -52,6 +53,8 @@ export async function build(options, config, projectOpts) {
|
|
|
52
53
|
compileEnvFiles(options);
|
|
53
54
|
// Compile workerd init
|
|
54
55
|
compileInit(options);
|
|
56
|
+
// Compile image helpers
|
|
57
|
+
compileImages(options);
|
|
55
58
|
// Compile middleware
|
|
56
59
|
await createMiddleware(options, { forceOnlyBuildOnce: true });
|
|
57
60
|
createStaticAssets(options, { useBasePath: true });
|
|
@@ -4,7 +4,7 @@ import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare function bundleServer(buildOpts: BuildOptions): Promise<void>;
|
|
6
6
|
/**
|
|
7
|
-
* This function
|
|
7
|
+
* This function apply updates to the bundled code.
|
|
8
8
|
*/
|
|
9
9
|
export declare function updateWorkerBundledCode(workerOutputFile: string): Promise<void>;
|
|
10
10
|
/**
|
|
@@ -8,7 +8,6 @@ import { build } from "esbuild";
|
|
|
8
8
|
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
|
-
import * as patches from "./patches/index.js";
|
|
12
11
|
import { inlineDynamicRequires } from "./patches/plugins/dynamic-requires.js";
|
|
13
12
|
import { inlineFindDir } from "./patches/plugins/find-dir.js";
|
|
14
13
|
import { patchInstrumentation } from "./patches/plugins/instrumentation.js";
|
|
@@ -21,7 +20,7 @@ import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations
|
|
|
21
20
|
import { fixRequire } from "./patches/plugins/require.js";
|
|
22
21
|
import { shimRequireHook } from "./patches/plugins/require-hook.js";
|
|
23
22
|
import { setWranglerExternal } from "./patches/plugins/wrangler-external.js";
|
|
24
|
-
import { needsExperimentalReact, normalizePath
|
|
23
|
+
import { copyPackageCliFiles, needsExperimentalReact, normalizePath } from "./utils/index.js";
|
|
25
24
|
/** The dist directory of the Cloudflare adapter package */
|
|
26
25
|
const packageDistDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
27
26
|
/**
|
|
@@ -41,7 +40,7 @@ const optionalDependencies = [
|
|
|
41
40
|
* Bundle the Open Next server.
|
|
42
41
|
*/
|
|
43
42
|
export async function bundleServer(buildOpts) {
|
|
44
|
-
|
|
43
|
+
copyPackageCliFiles(packageDistDir, buildOpts);
|
|
45
44
|
const { appPath, outputDir, monorepoRoot, debug } = buildOpts;
|
|
46
45
|
const baseManifestPath = path.join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
|
|
47
46
|
const serverFiles = path.join(baseManifestPath, "required-server-files.json");
|
|
@@ -142,13 +141,11 @@ export async function bundleServer(buildOpts) {
|
|
|
142
141
|
console.log(`\x1b[35mWorker saved in \`${path.relative(buildOpts.appPath, getOutputWorkerPath(buildOpts))}\` 🚀\n\x1b[0m`);
|
|
143
142
|
}
|
|
144
143
|
/**
|
|
145
|
-
* This function
|
|
144
|
+
* This function apply updates to the bundled code.
|
|
146
145
|
*/
|
|
147
146
|
export async function updateWorkerBundledCode(workerOutputFile) {
|
|
148
147
|
const code = await readFile(workerOutputFile, "utf8");
|
|
149
|
-
const patchedCode =
|
|
150
|
-
["require", patches.patchRequire, { isOptional: true }],
|
|
151
|
-
]);
|
|
148
|
+
const patchedCode = code.replace(/__require\d?\(/g, "require(").replace(/__require\d?\./g, "require.");
|
|
152
149
|
await writeFile(workerOutputFile, patchedCode);
|
|
153
150
|
}
|
|
154
151
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { build } from "esbuild";
|
|
5
|
+
/**
|
|
6
|
+
* Compiles the initialization code for the workerd runtime
|
|
7
|
+
*/
|
|
8
|
+
export async function compileImages(options) {
|
|
9
|
+
const currentDir = path.join(path.dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const templatesDir = path.join(currentDir, "../../templates");
|
|
11
|
+
const imagesPath = path.join(templatesDir, "images.js");
|
|
12
|
+
const imagesManifestPath = path.join(options.appBuildOutputPath, ".next/images-manifest.json");
|
|
13
|
+
const imagesManifest = fs.existsSync(imagesManifestPath)
|
|
14
|
+
? JSON.parse(fs.readFileSync(imagesManifestPath, { encoding: "utf-8" }))
|
|
15
|
+
: {};
|
|
16
|
+
await build({
|
|
17
|
+
entryPoints: [imagesPath],
|
|
18
|
+
outdir: path.join(options.outputDir, "cloudflare"),
|
|
19
|
+
bundle: false,
|
|
20
|
+
minify: false,
|
|
21
|
+
format: "esm",
|
|
22
|
+
target: "esnext",
|
|
23
|
+
platform: "node",
|
|
24
|
+
define: {
|
|
25
|
+
__IMAGES_REMOTE_PATTERNS__: JSON.stringify(imagesManifest?.images?.remotePatterns ?? []),
|
|
26
|
+
__IMAGES_LOCAL_PATTERNS__: JSON.stringify(imagesManifest?.images?.localPatterns ?? []),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -138,7 +138,6 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
138
138
|
awsPatches.patchNextServer,
|
|
139
139
|
awsPatches.patchEnvVars,
|
|
140
140
|
awsPatches.patchBackgroundRevalidation,
|
|
141
|
-
awsPatches.patchDropBabel,
|
|
142
141
|
// Cloudflare specific patches
|
|
143
142
|
patchResRevalidate,
|
|
144
143
|
patchUseCacheIO,
|
|
@@ -4,14 +4,12 @@
|
|
|
4
4
|
* Note: we will probably need to revisit the patches when the Next adapter API lands
|
|
5
5
|
*
|
|
6
6
|
* - Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd
|
|
7
|
-
* - Inline the middleware manifest
|
|
8
7
|
* - Override the cache and composable cache handlers
|
|
9
8
|
*/
|
|
10
9
|
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
11
10
|
import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
|
|
12
11
|
export declare function patchNextServer(updater: ContentUpdater, buildOpts: BuildOptions): Plugin;
|
|
13
12
|
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
13
|
/**
|
|
16
14
|
* The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
|
|
17
15
|
* Next.js would then do a dynamic require on a transformed version of the path to retrieve the
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
* Note: we will probably need to revisit the patches when the Next adapter API lands
|
|
5
5
|
*
|
|
6
6
|
* - Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd
|
|
7
|
-
* - Inline the middleware manifest
|
|
8
7
|
* - Override the cache and composable cache handlers
|
|
9
8
|
*/
|
|
10
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
11
9
|
import path from "node:path";
|
|
12
10
|
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
13
11
|
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
@@ -24,11 +22,6 @@ export function patchNextServer(updater, buildOpts) {
|
|
|
24
22
|
callback: async ({ contents }) => {
|
|
25
23
|
const { outputDir } = buildOpts;
|
|
26
24
|
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
25
|
const outputPath = path.join(outputDir, "server-functions/default");
|
|
33
26
|
const cacheHandler = path.join(outputPath, getPackagePath(buildOpts), "cache.cjs");
|
|
34
27
|
contents = patchCode(contents, createCacheHandlerRule(cacheHandler));
|
|
@@ -50,18 +43,6 @@ fix: |-
|
|
|
50
43
|
return process.env.NEXT_BUILD_ID;
|
|
51
44
|
}
|
|
52
45
|
`;
|
|
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
46
|
/**
|
|
66
47
|
* The cache handler used by Next.js is normally defined in the config file as a path. At runtime,
|
|
67
48
|
* Next.js would then do a dynamic require on a transformed version of the path to retrieve the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { getOutputWorkerPath } from "
|
|
3
|
+
import { getOutputWorkerPath } from "../bundle-server.js";
|
|
4
4
|
/**
|
|
5
5
|
* Copies
|
|
6
6
|
* - the template files present in the cloudflare adapter package to `.open-next/cloudflare-templates`
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the diff resulting of applying the `rule` to `src`.
|
|
3
|
+
*
|
|
4
|
+
* @param filename Filename used in the patch output
|
|
5
|
+
* @param src Content of the source code
|
|
6
|
+
* @param rule ASTgrep rule
|
|
7
|
+
* @returns diff in unified diff format
|
|
8
|
+
*/
|
|
9
|
+
export declare function computePatchDiff(filename: string, src: string, rule: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
2
|
+
import { createPatch } from "diff";
|
|
3
|
+
/**
|
|
4
|
+
* Compute the diff resulting of applying the `rule` to `src`.
|
|
5
|
+
*
|
|
6
|
+
* @param filename Filename used in the patch output
|
|
7
|
+
* @param src Content of the source code
|
|
8
|
+
* @param rule ASTgrep rule
|
|
9
|
+
* @returns diff in unified diff format
|
|
10
|
+
*/
|
|
11
|
+
export function computePatchDiff(filename, src, rule) {
|
|
12
|
+
const dst = patchCode(src, rule);
|
|
13
|
+
return createPatch(filename, src, dst);
|
|
14
|
+
}
|
|
@@ -60,7 +60,9 @@ export async function copyWorkerdPackages(options, nodePackages) {
|
|
|
60
60
|
const isNodeModuleRegex = getCrossPlatformPathRegex(`.*/node_modules/(?<pkg>.*)`, { escape: false });
|
|
61
61
|
// Copy full external packages when they use "workerd" build condition
|
|
62
62
|
const nextConfig = loadConfig(path.join(options.appBuildOutputPath, ".next"));
|
|
63
|
-
const externalPackages =
|
|
63
|
+
const externalPackages =
|
|
64
|
+
// @ts-expect-error In Next 14 its under experimental.serverComponentsExternalPackages
|
|
65
|
+
nextConfig.serverExternalPackages ?? nextConfig.experimental.serverComponentsExternalPackages ?? [];
|
|
64
66
|
for (const [src, dst] of nodePackages.entries()) {
|
|
65
67
|
try {
|
|
66
68
|
const pkgJson = JSON.parse(await fs.readFile(path.join(src, "package.json"), "utf8"));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type RemotePattern = {
|
|
2
|
+
protocol?: "http" | "https";
|
|
3
|
+
hostname: string;
|
|
4
|
+
port?: string;
|
|
5
|
+
pathname: string;
|
|
6
|
+
search?: string;
|
|
7
|
+
};
|
|
8
|
+
export type LocalPattern = {
|
|
9
|
+
pathname: string;
|
|
10
|
+
search?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Fetches an images.
|
|
14
|
+
*
|
|
15
|
+
* Local images (starting with a '/' as fetched using the passed fetcher).
|
|
16
|
+
* Remote images should match the configured remote patterns or a 404 response is returned.
|
|
17
|
+
*/
|
|
18
|
+
export declare function fetchImage(fetcher: Fetcher | undefined, imageUrl: string): Response | Promise<Response> | undefined;
|
|
19
|
+
export declare function matchRemotePattern(pattern: RemotePattern, url: URL): boolean;
|
|
20
|
+
export declare function matchLocalPattern(pattern: LocalPattern, url: URL): boolean;
|
|
21
|
+
declare global {
|
|
22
|
+
var __IMAGES_REMOTE_PATTERNS__: RemotePattern[];
|
|
23
|
+
var __IMAGES_LOCAL_PATTERNS__: LocalPattern[];
|
|
24
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches an images.
|
|
3
|
+
*
|
|
4
|
+
* Local images (starting with a '/' as fetched using the passed fetcher).
|
|
5
|
+
* Remote images should match the configured remote patterns or a 404 response is returned.
|
|
6
|
+
*/
|
|
7
|
+
export function fetchImage(fetcher, imageUrl) {
|
|
8
|
+
// https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/server/image-optimizer.ts#L208
|
|
9
|
+
if (!imageUrl || imageUrl.length > 3072 || imageUrl.startsWith("//")) {
|
|
10
|
+
return getUrlErrorResponse();
|
|
11
|
+
}
|
|
12
|
+
// Local
|
|
13
|
+
if (imageUrl.startsWith("/")) {
|
|
14
|
+
let pathname;
|
|
15
|
+
let url;
|
|
16
|
+
try {
|
|
17
|
+
// We only need pathname and search
|
|
18
|
+
url = new URL(imageUrl, "http://n");
|
|
19
|
+
pathname = decodeURIComponent(url.pathname);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return getUrlErrorResponse();
|
|
23
|
+
}
|
|
24
|
+
if (/\/_next\/image($|\/)/.test(pathname)) {
|
|
25
|
+
return getUrlErrorResponse();
|
|
26
|
+
}
|
|
27
|
+
// If localPatterns are not defined all local images are allowed.
|
|
28
|
+
if (__IMAGES_LOCAL_PATTERNS__.length > 0 &&
|
|
29
|
+
!__IMAGES_LOCAL_PATTERNS__.some((p) => matchLocalPattern(p, url))) {
|
|
30
|
+
return getUrlErrorResponse();
|
|
31
|
+
}
|
|
32
|
+
return fetcher?.fetch(`http://assets.local${imageUrl}`);
|
|
33
|
+
}
|
|
34
|
+
// Remote
|
|
35
|
+
let url;
|
|
36
|
+
try {
|
|
37
|
+
url = new URL(imageUrl);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return getUrlErrorResponse();
|
|
41
|
+
}
|
|
42
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
43
|
+
return getUrlErrorResponse();
|
|
44
|
+
}
|
|
45
|
+
// The remotePatterns is used to allow images from specific remote external paths and block all others.
|
|
46
|
+
if (!__IMAGES_REMOTE_PATTERNS__.some((p) => matchRemotePattern(p, url))) {
|
|
47
|
+
return getUrlErrorResponse();
|
|
48
|
+
}
|
|
49
|
+
return fetch(imageUrl, { cf: { cacheEverything: true } });
|
|
50
|
+
}
|
|
51
|
+
export function matchRemotePattern(pattern, url) {
|
|
52
|
+
// https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/shared/lib/match-remote-pattern.ts
|
|
53
|
+
if (pattern.protocol !== undefined &&
|
|
54
|
+
pattern.protocol.replace(/:$/, "") !== url.protocol.replace(/:$/, "")) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (pattern.port !== undefined && pattern.port !== url.port) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (pattern.hostname === undefined || !new RegExp(pattern.hostname).test(url.hostname)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (pattern.search !== undefined && pattern.search !== url.search) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// Should be the same as writeImagesManifest()
|
|
67
|
+
return new RegExp(pattern.pathname).test(url.pathname);
|
|
68
|
+
}
|
|
69
|
+
export function matchLocalPattern(pattern, url) {
|
|
70
|
+
// https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/shared/lib/match-local-pattern.ts
|
|
71
|
+
if (pattern.search !== undefined && pattern.search !== url.search) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return new RegExp(pattern.pathname).test(url.pathname);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @returns same error as Next.js when the url query parameter is not accepted.
|
|
78
|
+
*/
|
|
79
|
+
function getUrlErrorResponse() {
|
|
80
|
+
return new Response(`"url" parameter is not allowed`, { status: 400 });
|
|
81
|
+
}
|
|
82
|
+
/* eslint-enable no-var */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//@ts-expect-error: Will be resolved by wrangler build
|
|
2
|
+
import { fetchImage } from "./cloudflare/images.js";
|
|
3
|
+
//@ts-expect-error: Will be resolved by wrangler build
|
|
2
4
|
import { runWithCloudflareRequestContext } from "./cloudflare/init.js";
|
|
3
5
|
// @ts-expect-error: Will be resolved by wrangler build
|
|
4
6
|
import { handler as middlewareHandler } from "./middleware/handler.mjs";
|
|
@@ -27,9 +29,7 @@ export default {
|
|
|
27
29
|
// Fallback for the Next default image loader.
|
|
28
30
|
if (url.pathname === `${globalThis.__NEXT_BASE_PATH__}/_next/image`) {
|
|
29
31
|
const imageUrl = url.searchParams.get("url") ?? "";
|
|
30
|
-
return imageUrl
|
|
31
|
-
? env.ASSETS?.fetch(`http://assets.local${imageUrl}`)
|
|
32
|
-
: fetch(imageUrl, { cf: { cacheEverything: true } });
|
|
32
|
+
return fetchImage(env.ASSETS, imageUrl);
|
|
33
33
|
}
|
|
34
34
|
// - `Request`s are handled by the Next server
|
|
35
35
|
const reqOrResp = await middlewareHandler(request, env, ctx);
|
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.
|
|
4
|
+
"version": "1.3.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.6",
|
|
47
47
|
"enquirer": "^2.4.1",
|
|
48
48
|
"glob": "^11.0.0",
|
|
49
49
|
"ts-tqdm": "^0.8.6"
|
|
@@ -54,6 +54,8 @@
|
|
|
54
54
|
"@tsconfig/strictest": "^2.0.5",
|
|
55
55
|
"@types/mock-fs": "^4.13.4",
|
|
56
56
|
"@types/node": "^22.2.0",
|
|
57
|
+
"@types/picomatch": "^4.0.0",
|
|
58
|
+
"diff": "^8.0.2",
|
|
57
59
|
"esbuild": "^0.25.4",
|
|
58
60
|
"eslint": "^9.11.1",
|
|
59
61
|
"eslint-plugin-import": "^2.31.0",
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
"globals": "^15.9.0",
|
|
63
65
|
"mock-fs": "^5.4.1",
|
|
64
66
|
"next": "~14.2.24",
|
|
67
|
+
"picomatch": "^4.0.2",
|
|
65
68
|
"rimraf": "^6.0.1",
|
|
66
69
|
"typescript": "^5.7.3",
|
|
67
70
|
"typescript-eslint": "^8.7.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import * as internal from "../overrides/internal";
|
|
3
|
-
import { BucketCachePurge } from "./bucket-cache-purge";
|
|
4
|
-
vi.mock("cloudflare:workers", () => ({
|
|
5
|
-
DurableObject: class {
|
|
6
|
-
ctx;
|
|
7
|
-
env;
|
|
8
|
-
constructor(ctx, env) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
this.env = env;
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
}));
|
|
14
|
-
const createBucketCachePurge = () => {
|
|
15
|
-
const mockState = {
|
|
16
|
-
waitUntil: vi.fn(),
|
|
17
|
-
blockConcurrencyWhile: vi.fn().mockImplementation(async (fn) => fn()),
|
|
18
|
-
storage: {
|
|
19
|
-
setAlarm: vi.fn(),
|
|
20
|
-
getAlarm: vi.fn(),
|
|
21
|
-
sql: {
|
|
22
|
-
exec: vi.fn().mockImplementation(() => ({
|
|
23
|
-
one: vi.fn(),
|
|
24
|
-
toArray: vi.fn().mockReturnValue([]),
|
|
25
|
-
})),
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
-
return new BucketCachePurge(mockState, {});
|
|
31
|
-
};
|
|
32
|
-
describe("BucketCachePurge", () => {
|
|
33
|
-
it("should block concurrency while creating the table", async () => {
|
|
34
|
-
const cache = createBucketCachePurge();
|
|
35
|
-
// @ts-expect-error - testing private method
|
|
36
|
-
expect(cache.ctx.blockConcurrencyWhile).toHaveBeenCalled();
|
|
37
|
-
// @ts-expect-error - testing private method
|
|
38
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledWith(expect.stringContaining("CREATE TABLE IF NOT EXISTS cache_purge"));
|
|
39
|
-
});
|
|
40
|
-
describe("purgeCacheByTags", () => {
|
|
41
|
-
it("should insert tags into the sql table", async () => {
|
|
42
|
-
const cache = createBucketCachePurge();
|
|
43
|
-
const tags = ["tag1", "tag2"];
|
|
44
|
-
await cache.purgeCacheByTags(tags);
|
|
45
|
-
// @ts-expect-error - testing private method
|
|
46
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledWith(expect.stringContaining("INSERT OR REPLACE INTO cache_purge"), [tags[0]]);
|
|
47
|
-
// @ts-expect-error - testing private method
|
|
48
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledWith(expect.stringContaining("INSERT OR REPLACE INTO cache_purge"), [tags[1]]);
|
|
49
|
-
});
|
|
50
|
-
it("should set an alarm if no alarm is set", async () => {
|
|
51
|
-
const cache = createBucketCachePurge();
|
|
52
|
-
// @ts-expect-error - testing private method
|
|
53
|
-
cache.ctx.storage.getAlarm.mockResolvedValueOnce(null);
|
|
54
|
-
await cache.purgeCacheByTags(["tag"]);
|
|
55
|
-
// @ts-expect-error - testing private method
|
|
56
|
-
expect(cache.ctx.storage.setAlarm).toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
it("should not set an alarm if one is already set", async () => {
|
|
59
|
-
const cache = createBucketCachePurge();
|
|
60
|
-
// @ts-expect-error - testing private method
|
|
61
|
-
cache.ctx.storage.getAlarm.mockResolvedValueOnce(true);
|
|
62
|
-
await cache.purgeCacheByTags(["tag"]);
|
|
63
|
-
// @ts-expect-error - testing private method
|
|
64
|
-
expect(cache.ctx.storage.setAlarm).not.toHaveBeenCalled();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe("alarm", () => {
|
|
68
|
-
it("should purge cache by tags and delete them from the sql table", async () => {
|
|
69
|
-
const cache = createBucketCachePurge();
|
|
70
|
-
// @ts-expect-error - testing private method
|
|
71
|
-
cache.ctx.storage.sql.exec.mockReturnValueOnce({
|
|
72
|
-
toArray: () => [{ tag: "tag1" }, { tag: "tag2" }],
|
|
73
|
-
});
|
|
74
|
-
await cache.alarm();
|
|
75
|
-
// @ts-expect-error - testing private method
|
|
76
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledWith(expect.stringContaining("DELETE FROM cache_purge"), ["tag1", "tag2"]);
|
|
77
|
-
});
|
|
78
|
-
it("should not purge cache if no tags are found", async () => {
|
|
79
|
-
const cache = createBucketCachePurge();
|
|
80
|
-
// @ts-expect-error - testing private method
|
|
81
|
-
cache.ctx.storage.sql.exec.mockReturnValueOnce({
|
|
82
|
-
toArray: () => [],
|
|
83
|
-
});
|
|
84
|
-
await cache.alarm();
|
|
85
|
-
// @ts-expect-error - testing private method
|
|
86
|
-
expect(cache.ctx.storage.sql.exec).not.toHaveBeenCalledWith(expect.stringContaining("DELETE FROM cache_purge"), []);
|
|
87
|
-
});
|
|
88
|
-
it("should call internalPurgeCacheByTags with the correct tags", async () => {
|
|
89
|
-
const cache = createBucketCachePurge();
|
|
90
|
-
const tags = ["tag1", "tag2"];
|
|
91
|
-
// @ts-expect-error - testing private method
|
|
92
|
-
cache.ctx.storage.sql.exec.mockReturnValueOnce({
|
|
93
|
-
toArray: () => tags.map((tag) => ({ tag })),
|
|
94
|
-
});
|
|
95
|
-
const internalPurgeCacheByTagsSpy = vi.spyOn(internal, "internalPurgeCacheByTags");
|
|
96
|
-
await cache.alarm();
|
|
97
|
-
expect(internalPurgeCacheByTagsSpy).toHaveBeenCalledWith(
|
|
98
|
-
// @ts-expect-error - testing private method
|
|
99
|
-
cache.env, tags);
|
|
100
|
-
// @ts-expect-error - testing private method 1st is constructor, 2nd is to get the tags and 3rd is to delete them
|
|
101
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledTimes(3);
|
|
102
|
-
});
|
|
103
|
-
it("should continue until all tags are purged", async () => {
|
|
104
|
-
const cache = createBucketCachePurge();
|
|
105
|
-
const tags = Array.from({ length: 100 }, (_, i) => `tag${i}`);
|
|
106
|
-
// @ts-expect-error - testing private method
|
|
107
|
-
cache.ctx.storage.sql.exec.mockReturnValueOnce({
|
|
108
|
-
toArray: () => tags.map((tag) => ({ tag })),
|
|
109
|
-
});
|
|
110
|
-
const internalPurgeCacheByTagsSpy = vi.spyOn(internal, "internalPurgeCacheByTags");
|
|
111
|
-
await cache.alarm();
|
|
112
|
-
expect(internalPurgeCacheByTagsSpy).toHaveBeenCalledWith(
|
|
113
|
-
// @ts-expect-error - testing private method
|
|
114
|
-
cache.env, tags);
|
|
115
|
-
// @ts-expect-error - testing private method 1st is constructor, 2nd is to get the tags and 3rd is to delete them, 4th is to get the next 100 tags
|
|
116
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenCalledTimes(4);
|
|
117
|
-
// @ts-expect-error - testing private method
|
|
118
|
-
expect(cache.ctx.storage.sql.exec).toHaveBeenLastCalledWith(expect.stringContaining("SELECT * FROM cache_purge LIMIT 100"));
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|