Skip to content

Commit e8df68e

Browse files
authored
Allow setting d1 database id in pages dev (#3485)
* Allow setting d1 database id in pages dev * Allow KV and R2 to be referenced to too * Fix state directory in fixture test * Add comments describing the binding regexps * Remove duplicate import * More defensively parse bindings
1 parent d199ffb commit e8df68e

File tree

5 files changed

+122
-17
lines changed

5 files changed

+122
-17
lines changed

.changeset/weak-dolphins-carry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
feat: Allow setting a D1 database ID when using `wrangler pages dev` by providing an optional `=<ID>` suffix to the argument like `--d1 BINDING_NAME=database-id`

fixtures/pages-workerjs-directory/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"sideEffects": false,
66
"scripts": {
77
"check:type": "tsc",
8-
"dev": "npx wrangler pages dev public --port 8794",
8+
"dev": "npx wrangler pages dev public --d1=D1 --d1=PUT=elsewhere --kv KV --kv KV_REF=other_kv --r2 R2 --r2=R2_REF=other_r2 --port 8794",
99
"test": "npx vitest run",
1010
"test:ci": "npx vitest run",
1111
"type:tests": "tsc -p ./tests/tsconfig.json"

fixtures/pages-workerjs-directory/public/_worker.js/index.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,33 @@ export default {
1515
}
1616

1717
if (pathname === "/d1") {
18-
const stmt = env.D1.prepare("SELECT 1");
18+
const stmt1 = env.D1.prepare("SELECT 1");
19+
const values1 = await stmt1.first();
20+
21+
const stmt = env.PUT.prepare("SELECT 1");
1922
const values = await stmt.first();
20-
return new Response(JSON.stringify(values));
23+
24+
if (JSON.stringify(values1) === JSON.stringify(values)) {
25+
return new Response(JSON.stringify(values));
26+
}
27+
28+
return new Response("couldn't select 1");
29+
}
30+
31+
if (pathname === "/kv") {
32+
await env.KV.put("key", "value");
33+
34+
await env.KV_REF.put("key", "value");
35+
36+
return new Response("saved");
37+
}
38+
39+
if (pathname === "/r2") {
40+
await env.R2.put("key", "value");
41+
42+
await env.R2_REF.put("key", "value");
43+
44+
return new Response("saved");
2145
}
2246

2347
if (pathname !== "/") {

fixtures/pages-workerjs-directory/tests/index.test.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { execSync } from "node:child_process";
2-
import { readFileSync } from "node:fs";
2+
import { existsSync, readFileSync } from "node:fs";
33
import { tmpdir } from "node:os";
44
import path, { join, resolve } from "node:path";
55
import { fetch } from "undici";
@@ -8,10 +8,21 @@ import { runWranglerPagesDev } from "../../shared/src/run-wrangler-long-lived";
88

99
describe("Pages _worker.js/ directory", () => {
1010
it("should support non-bundling with 'dev'", async ({ expect }) => {
11+
const tmpDir = join(tmpdir(), Math.random().toString(36).slice(2));
12+
1113
const { ip, port, stop } = await runWranglerPagesDev(
1214
resolve(__dirname, ".."),
1315
"public",
14-
["--port=0", "--d1=D1"]
16+
[
17+
"--port=0",
18+
`--persist-to=${tmpDir}`,
19+
"--d1=D1",
20+
"--d1=PUT=elsewhere",
21+
"--kv=KV",
22+
"--kv=KV_REF=other_kv",
23+
"--r2=R2",
24+
"--r2=R2_REF=other_r2",
25+
]
1526
);
1627
await expect(
1728
fetch(`http://${ip}:${port}/`).then((resp) => resp.text())
@@ -28,7 +39,20 @@ describe("Pages _worker.js/ directory", () => {
2839
await expect(
2940
fetch(`http://${ip}:${port}/d1`).then((resp) => resp.text())
3041
).resolves.toContain('{"1":1}');
42+
await expect(
43+
fetch(`http://${ip}:${port}/kv`).then((resp) => resp.text())
44+
).resolves.toContain("saved");
45+
await expect(
46+
fetch(`http://${ip}:${port}/r2`).then((resp) => resp.text())
47+
).resolves.toContain("saved");
3148
await stop();
49+
50+
expect(existsSync(join(tmpDir, "./v3/d1/D1"))).toBeTruthy();
51+
expect(existsSync(join(tmpDir, "./v3/d1/elsewhere"))).toBeTruthy();
52+
expect(existsSync(join(tmpDir, "./v3/kv/KV"))).toBeTruthy();
53+
expect(existsSync(join(tmpDir, "./v3/kv/other_kv"))).toBeTruthy();
54+
expect(existsSync(join(tmpDir, "./v3/r2/R2"))).toBeTruthy();
55+
expect(existsSync(join(tmpDir, "./v3/r2/other_r2"))).toBeTruthy();
3256
});
3357

3458
it("should bundle", async ({ expect }) => {

packages/wrangler/src/pages/dev.ts

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,28 @@ import type {
2828
} from "../yargs-types";
2929
import type { RoutesJSONSpec } from "./functions/routes-transformation";
3030

31+
/*
32+
* DURABLE_OBJECTS_BINDING_REGEXP matches strings like:
33+
* - "binding=className"
34+
* - "BINDING=MyClass"
35+
* - "BINDING=MyClass@service-name"
36+
* Every DO needs a binding (the JS reference) and the exported class name it refers to.
37+
* Optionally, users can also provide a service name if they want to reference a DO from another dev session over the dev registry.
38+
*/
3139
const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
3240
/^(?<binding>[^=]+)=(?<className>[^@\s]+)(@(?<scriptName>.*)$)?$/
3341
);
3442

43+
/* BINDING_REGEXP matches strings like:
44+
* - "binding"
45+
* - "BINDING"
46+
* - "BINDING=ref"
47+
* This is used to capture both the binding name (how the binding is used in JS) as well as the reference if provided.
48+
* In the case of a D1 database, that's the database ID.
49+
* This is useful to people who want to reference the same database in multiple bindings, or a Worker and Pages project dev session want to reference the same database.
50+
*/
51+
const BINDING_REGEXP = new RegExp(/^(?<binding>[^=]+)(?:=(?<ref>[^\s]+))?$/);
52+
3553
export function Options(yargs: CommonYargsArgv) {
3654
return yargs
3755
.positional("directory", {
@@ -520,10 +538,22 @@ export const Handler = async ({
520538
.map((binding) => binding.toString().split("="))
521539
.map(([key, ...values]) => [key, values.join("=")])
522540
),
523-
kv: kvs.map((binding) => ({
524-
binding: binding.toString(),
525-
id: binding.toString(),
526-
})),
541+
kv: kvs
542+
.map((kv) => {
543+
const { binding, ref } =
544+
BINDING_REGEXP.exec(kv.toString())?.groups || {};
545+
546+
if (!binding) {
547+
logger.warn("Could not parse KV binding:", kv.toString());
548+
return;
549+
}
550+
551+
return {
552+
binding,
553+
id: ref || kv.toString(),
554+
};
555+
})
556+
.filter(Boolean) as AdditionalDevProps["kv"],
527557
durableObjects: durableObjects
528558
.map((durableObject) => {
529559
const { binding, className, scriptName } =
@@ -545,9 +575,19 @@ export const Handler = async ({
545575
};
546576
})
547577
.filter(Boolean) as AdditionalDevProps["durableObjects"],
548-
r2: r2s.map((binding) => {
549-
return { binding: binding.toString(), bucket_name: binding.toString() };
550-
}),
578+
r2: r2s
579+
.map((r2) => {
580+
const { binding, ref } =
581+
BINDING_REGEXP.exec(r2.toString())?.groups || {};
582+
583+
if (!binding) {
584+
logger.warn("Could not parse R2 binding:", r2.toString());
585+
return;
586+
}
587+
588+
return { binding, bucket_name: ref || binding.toString() };
589+
})
590+
.filter(Boolean) as AdditionalDevProps["r2"],
551591
rules: usingWorkerDirectory
552592
? [
553593
{
@@ -563,11 +603,23 @@ export const Handler = async ({
563603
experimental: {
564604
processEntrypoint: true,
565605
additionalModules: modules,
566-
d1Databases: d1s.map((binding) => ({
567-
binding: binding.toString(),
568-
database_id: binding.toString(),
569-
database_name: `local-${binding}`,
570-
})),
606+
d1Databases: d1s
607+
.map((d1) => {
608+
const { binding, ref } =
609+
BINDING_REGEXP.exec(d1.toString())?.groups || {};
610+
611+
if (!binding) {
612+
logger.warn("Could not parse D1 binding:", d1.toString());
613+
return;
614+
}
615+
616+
return {
617+
binding,
618+
database_id: ref || d1.toString(),
619+
database_name: `local-${d1}`,
620+
};
621+
})
622+
.filter(Boolean) as AdditionalDevProps["d1Databases"],
571623
disableExperimentalWarning: true,
572624
enablePagesAssetsServiceBinding: {
573625
proxyPort,

0 commit comments

Comments
 (0)