Skip to content

Commit a8cdce1

Browse files
committed
feat: auto-populating d1 cache data
1 parent ff5cd39 commit a8cdce1

File tree

8 files changed

+127
-24
lines changed

8 files changed

+127
-24
lines changed

.changeset/tame-icons-shave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
feat: auto-populating d1 cache data

examples/e2e/app-router/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
"lint": "next lint",
1111
"clean": "rm -rf .turbo node_modules .next .open-next",
1212
"d1:clean": "wrangler d1 execute NEXT_CACHE_D1 --command \"DROP TABLE IF EXISTS tags; DROP TABLE IF EXISTS revalidations\"",
13-
"d1:setup": "wrangler d1 execute NEXT_CACHE_D1 --file .open-next/cloudflare/cache-assets-manifest.sql",
14-
"build:worker": "pnpm opennextjs-cloudflare && pnpm d1:clean && pnpm d1:setup",
13+
"build:worker": "pnpm d1:clean && pnpm opennextjs-cloudflare --populateCache=local",
1514
"preview": "pnpm build:worker && pnpm wrangler dev",
1615
"e2e": "playwright test -c e2e/playwright.config.ts"
1716
},

packages/cloudflare/src/cli/args.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,69 @@ import { mkdirSync, type Stats, statSync } from "node:fs";
22
import { resolve } from "node:path";
33
import { parseArgs } from "node:util";
44

5+
import { CacheBindingMode } from "./build/utils/index.js";
6+
57
export function getArgs(): {
68
skipNextBuild: boolean;
79
skipWranglerConfigCheck: boolean;
810
outputDir?: string;
911
minify: boolean;
12+
populateCache?: { mode: "local" | "remote"; onlyPopulate: boolean };
1013
} {
11-
const { skipBuild, skipWranglerConfigCheck, output, noMinify } = parseArgs({
12-
options: {
13-
skipBuild: {
14-
type: "boolean",
15-
short: "s",
16-
default: false,
17-
},
18-
output: {
19-
type: "string",
20-
short: "o",
21-
},
22-
noMinify: {
23-
type: "boolean",
24-
default: false,
14+
const { skipBuild, skipWranglerConfigCheck, output, noMinify, populateCache, onlyPopulateCache } =
15+
parseArgs({
16+
options: {
17+
skipBuild: {
18+
type: "boolean",
19+
short: "s",
20+
default: false,
21+
},
22+
output: {
23+
type: "string",
24+
short: "o",
25+
},
26+
noMinify: {
27+
type: "boolean",
28+
default: false,
29+
},
30+
skipWranglerConfigCheck: {
31+
type: "boolean",
32+
default: false,
33+
},
34+
populateCache: {
35+
type: "string",
36+
},
37+
onlyPopulateCache: {
38+
type: "boolean",
39+
default: false,
40+
},
2541
},
26-
skipWranglerConfigCheck: {
27-
type: "boolean",
28-
default: false,
29-
},
30-
},
31-
allowPositionals: false,
32-
}).values;
42+
allowPositionals: false,
43+
}).values;
3344

3445
const outputDir = output ? resolve(output) : undefined;
3546

3647
if (outputDir) {
3748
assertDirArg(outputDir, "output", true);
3849
}
3950

51+
if (
52+
(populateCache !== undefined || onlyPopulateCache) &&
53+
(!populateCache?.length || !["local", "remote"].includes(populateCache))
54+
) {
55+
throw new Error(`Error: missing mode for populate cache flag, expected 'local' | 'remote'`);
56+
}
57+
4058
return {
4159
outputDir,
4260
skipNextBuild: skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
4361
skipWranglerConfigCheck:
4462
skipWranglerConfigCheck ||
4563
["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)),
4664
minify: !noMinify,
65+
populateCache: populateCache
66+
? { mode: populateCache as CacheBindingMode, onlyPopulate: !!onlyPopulateCache }
67+
: undefined,
4768
};
4869
}
4970

packages/cloudflare/src/cli/build/build.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
createOpenNextConfigIfNotExistent,
2121
createWranglerConfigIfNotExistent,
2222
ensureCloudflareConfig,
23+
populateCache,
2324
} from "./utils/index.js";
2425
import { getVersion } from "./utils/version.js";
2526

@@ -62,6 +63,11 @@ export async function build(projectOpts: ProjectOptions): Promise<void> {
6263
logger.info(`@opennextjs/cloudflare version: ${cloudflare}`);
6364
logger.info(`@opennextjs/aws version: ${aws}`);
6465

66+
if (projectOpts.populateCache?.onlyPopulate) {
67+
populateCache(options, config, projectOpts.populateCache.mode);
68+
return;
69+
}
70+
6571
if (projectOpts.skipNextBuild) {
6672
logger.warn("Skipping Next.js build");
6773
} else {
@@ -103,6 +109,10 @@ export async function build(projectOpts: ProjectOptions): Promise<void> {
103109
await createWranglerConfigIfNotExistent(projectOpts);
104110
}
105111

112+
if (projectOpts.populateCache) {
113+
populateCache(options, config, projectOpts.populateCache.mode);
114+
}
115+
106116
logger.info("OpenNext build complete.");
107117
}
108118

packages/cloudflare/src/cli/build/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from "./create-config-files.js";
33
export * from "./ensure-cf-config.js";
44
export * from "./extract-project-env-vars.js";
55
export * from "./normalize-path.js";
6+
export * from "./populate-cache.js";
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { spawnSync, SpawnSyncReturns } from "node:child_process";
2+
import path from "node:path";
3+
4+
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
5+
import logger from "@opennextjs/aws/logger.js";
6+
import type {
7+
IncludedIncrementalCache,
8+
IncludedTagCache,
9+
LazyLoadedOverride,
10+
OpenNextConfig,
11+
} from "@opennextjs/aws/types/open-next.js";
12+
import type { IncrementalCache, TagCache } from "@opennextjs/aws/types/overrides.js";
13+
14+
export type CacheBindingMode = "local" | "remote";
15+
16+
async function resolveCacheName(
17+
value:
18+
| IncludedIncrementalCache
19+
| IncludedTagCache
20+
| LazyLoadedOverride<IncrementalCache>
21+
| LazyLoadedOverride<TagCache>
22+
) {
23+
return typeof value === "function" ? (await value()).name : value;
24+
}
25+
26+
function withMode(mode: CacheBindingMode, args: string[]) {
27+
return [...args, mode === "remote" && "--remote"].filter((v): v is string => !!v);
28+
}
29+
30+
function logResult(result: SpawnSyncReturns<Buffer>) {
31+
if (result.status !== 0) {
32+
logger.error("Failed to populate cache");
33+
} else {
34+
logger.info("Successfully populated cache");
35+
}
36+
}
37+
38+
export async function populateCache(options: BuildOptions, config: OpenNextConfig, mode: CacheBindingMode) {
39+
const { tagCache } = config.default.override ?? {};
40+
41+
if (tagCache) {
42+
const name = await resolveCacheName(tagCache);
43+
switch (name) {
44+
case "d1-tag-cache": {
45+
logger.info("\nPopulating D1 tag cache...");
46+
47+
const result = spawnSync(
48+
options.packager,
49+
withMode(mode, [
50+
"exec",
51+
"wrangler d1 execute",
52+
"NEXT_CACHE_D1",
53+
`--file ${JSON.stringify(path.join(options.outputDir, "cloudflare/cache-assets-manifest.sql"))}`,
54+
]),
55+
{ shell: true, stdio: ["ignore", "ignore", "inherit"] }
56+
);
57+
58+
logResult(result);
59+
break;
60+
}
61+
}
62+
}
63+
}

packages/cloudflare/src/cli/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import { build } from "./build/build.js";
66

77
const nextAppDir = process.cwd();
88

9-
const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify } = getArgs();
9+
const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify, populateCache } = getArgs();
1010

1111
await build({
1212
sourceDir: nextAppDir,
1313
outputDir: resolve(outputDir ?? nextAppDir, ".open-next"),
1414
skipNextBuild,
1515
skipWranglerConfigCheck,
1616
minify,
17+
populateCache,
1718
});

packages/cloudflare/src/cli/project-options.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { CacheBindingMode } from "./build/utils/index.js";
2+
13
export type ProjectOptions = {
24
// Next app root folder
35
sourceDir: string;
@@ -9,4 +11,5 @@ export type ProjectOptions = {
911
skipWranglerConfigCheck: boolean;
1012
// Whether minification of the worker should be enabled
1113
minify: boolean;
14+
populateCache?: { mode: CacheBindingMode; onlyPopulate: boolean };
1215
};

0 commit comments

Comments
 (0)