wrangler 2.13.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -7
- package/src/__tests__/api-devregistry.test.ts +121 -0
- package/src/__tests__/api.test.ts +27 -5
- package/src/__tests__/configuration.test.ts +45 -0
- package/src/__tests__/pages/publish.test.ts +67 -23
- package/src/__tests__/traverse-module-graph.test.ts +220 -0
- package/src/api/dev.ts +7 -18
- package/src/api/pages/create-worker-bundle-contents.ts +1 -0
- package/src/bundle.ts +9 -5
- package/src/config/environment.ts +24 -0
- package/src/config/index.ts +18 -0
- package/src/config/validation.ts +88 -0
- package/src/create-worker-upload-form.ts +17 -0
- package/src/d1/execute.tsx +1 -1
- package/src/dev/start-server.ts +2 -8
- package/src/dev/use-esbuild.ts +2 -8
- package/src/dev-registry.ts +2 -1
- package/src/dev.tsx +1 -0
- package/src/entry.ts +18 -8
- package/src/module-collection.ts +91 -25
- package/src/pages/functions/buildPlugin.ts +1 -0
- package/src/pages/functions/buildWorker.ts +2 -8
- package/src/publish/publish.ts +3 -8
- package/src/secret/index.ts +1 -0
- package/src/traverse-module-graph.ts +53 -0
- package/src/worker.ts +10 -0
- package/wrangler-dist/cli.d.ts +22 -0
- package/wrangler-dist/cli.js +1906 -1725
- package/src/__tests__/api-devregistry.test.js +0 -64
- package/src/__tests__/tsconfig.tsbuildinfo +0 -1
- package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
- package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
- package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
- package/templates/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import dedent from "ts-dedent";
|
|
4
|
+
import traverseModuleGraph from "../traverse-module-graph";
|
|
5
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
6
|
+
import type { ConfigModuleRuleType } from "../config";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* This file contains inline comments with the word "javascript"
|
|
10
|
+
* This signals to a compatible editor extension that the template string
|
|
11
|
+
* contents should be syntax-highlighted as JavaScript. One such extension
|
|
12
|
+
* is zjcompt.es6-string-javascript, but there are others.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
describe("traverse module graph", () => {
|
|
16
|
+
runInTempDir();
|
|
17
|
+
|
|
18
|
+
it("should not detect JS without module rules", async () => {
|
|
19
|
+
await writeFile(
|
|
20
|
+
"./index.js",
|
|
21
|
+
dedent/* javascript */ `
|
|
22
|
+
import { HELLO } from "./other.js"
|
|
23
|
+
export default {
|
|
24
|
+
async fetch(request) {
|
|
25
|
+
return new Response(HELLO)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
);
|
|
30
|
+
await writeFile(
|
|
31
|
+
"./other.js",
|
|
32
|
+
dedent/* javascript */ `
|
|
33
|
+
export const HELLO = "WORLD"
|
|
34
|
+
`
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const bundle = await traverseModuleGraph(
|
|
38
|
+
{
|
|
39
|
+
file: path.join(process.cwd(), "./index.js"),
|
|
40
|
+
directory: process.cwd(),
|
|
41
|
+
format: "modules",
|
|
42
|
+
moduleRoot: process.cwd(),
|
|
43
|
+
},
|
|
44
|
+
[]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(bundle.modules).toStrictEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it.each([
|
|
51
|
+
["ESModule", "esm"],
|
|
52
|
+
["CommonJS", "commonjs"],
|
|
53
|
+
])("should detect JS as %s", async (type, format) => {
|
|
54
|
+
await writeFile(
|
|
55
|
+
"./index.js",
|
|
56
|
+
dedent/* javascript */ `
|
|
57
|
+
import { HELLO } from "./other.js"
|
|
58
|
+
export default {
|
|
59
|
+
async fetch(request) {
|
|
60
|
+
return new Response(HELLO)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
`
|
|
64
|
+
);
|
|
65
|
+
await writeFile(
|
|
66
|
+
"./other.js",
|
|
67
|
+
dedent/* javascript */ `
|
|
68
|
+
export const HELLO = "WORLD"
|
|
69
|
+
`
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const bundle = await traverseModuleGraph(
|
|
73
|
+
{
|
|
74
|
+
file: path.join(process.cwd(), "./index.js"),
|
|
75
|
+
directory: process.cwd(),
|
|
76
|
+
format: "modules",
|
|
77
|
+
moduleRoot: process.cwd(),
|
|
78
|
+
},
|
|
79
|
+
[{ type: type as ConfigModuleRuleType, globs: ["**/*.js"] }]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(bundle.modules[0].type).toStrictEqual(format);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should not resolve JS outside the module root", async () => {
|
|
86
|
+
await mkdir("./src/nested", { recursive: true });
|
|
87
|
+
await writeFile(
|
|
88
|
+
"./src/nested/index.js",
|
|
89
|
+
dedent/* javascript */ `
|
|
90
|
+
import { HELLO } from "../other.js"
|
|
91
|
+
export default {
|
|
92
|
+
async fetch(request) {
|
|
93
|
+
return new Response(HELLO)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`
|
|
97
|
+
);
|
|
98
|
+
await writeFile(
|
|
99
|
+
"./src/other.js",
|
|
100
|
+
dedent/* javascript */ `
|
|
101
|
+
export const HELLO = "WORLD"
|
|
102
|
+
`
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const bundle = await traverseModuleGraph(
|
|
106
|
+
{
|
|
107
|
+
file: path.join(process.cwd(), "./src/nested/index.js"),
|
|
108
|
+
directory: path.join(process.cwd(), "./src/nested"),
|
|
109
|
+
format: "modules",
|
|
110
|
+
// The default module root is dirname(file)
|
|
111
|
+
moduleRoot: path.join(process.cwd(), "./src/nested"),
|
|
112
|
+
},
|
|
113
|
+
[{ type: "ESModule", globs: ["**/*.js"] }]
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(bundle.modules).toStrictEqual([]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should resolve JS with module root", async () => {
|
|
120
|
+
await mkdir("./src/nested", { recursive: true });
|
|
121
|
+
await writeFile(
|
|
122
|
+
"./src/nested/index.js",
|
|
123
|
+
dedent/* javascript */ `
|
|
124
|
+
import { HELLO } from "../other.js"
|
|
125
|
+
export default {
|
|
126
|
+
async fetch(request) {
|
|
127
|
+
return new Response(HELLO)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`
|
|
131
|
+
);
|
|
132
|
+
await writeFile(
|
|
133
|
+
"./src/other.js",
|
|
134
|
+
dedent/* javascript */ `
|
|
135
|
+
export const HELLO = "WORLD"
|
|
136
|
+
`
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const bundle = await traverseModuleGraph(
|
|
140
|
+
{
|
|
141
|
+
file: path.join(process.cwd(), "./src/nested/index.js"),
|
|
142
|
+
directory: path.join(process.cwd(), "./src/nested"),
|
|
143
|
+
format: "modules",
|
|
144
|
+
// The default module root is dirname(file)
|
|
145
|
+
moduleRoot: path.join(process.cwd(), "./src"),
|
|
146
|
+
},
|
|
147
|
+
[{ type: "ESModule", globs: ["**/*.js"] }]
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(bundle.modules[0].name).toStrictEqual("other.js");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should ignore files not matched by glob", async () => {
|
|
154
|
+
await mkdir("./src/nested", { recursive: true });
|
|
155
|
+
await writeFile(
|
|
156
|
+
"./src/nested/index.js",
|
|
157
|
+
dedent/* javascript */ `
|
|
158
|
+
import { HELLO } from "../other.js"
|
|
159
|
+
export default {
|
|
160
|
+
async fetch(request) {
|
|
161
|
+
return new Response(HELLO)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
`
|
|
165
|
+
);
|
|
166
|
+
await writeFile(
|
|
167
|
+
"./src/other.js",
|
|
168
|
+
dedent/* javascript */ `
|
|
169
|
+
export const HELLO = "WORLD"
|
|
170
|
+
`
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const bundle = await traverseModuleGraph(
|
|
174
|
+
{
|
|
175
|
+
file: path.join(process.cwd(), "./src/nested/index.js"),
|
|
176
|
+
directory: path.join(process.cwd(), "./src/nested"),
|
|
177
|
+
format: "modules",
|
|
178
|
+
// The default module root is dirname(file)
|
|
179
|
+
moduleRoot: path.join(process.cwd(), "./src"),
|
|
180
|
+
},
|
|
181
|
+
[{ type: "ESModule", globs: ["**/*.mjs"] }]
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(bundle.modules.length).toStrictEqual(0);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should resolve files that match the default rules", async () => {
|
|
188
|
+
await mkdir("./src", { recursive: true });
|
|
189
|
+
await writeFile(
|
|
190
|
+
"./src/index.js",
|
|
191
|
+
dedent/* javascript */ `
|
|
192
|
+
import HELLO from "../other.txt"
|
|
193
|
+
export default {
|
|
194
|
+
async fetch(request) {
|
|
195
|
+
return new Response(HELLO)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`
|
|
199
|
+
);
|
|
200
|
+
await writeFile(
|
|
201
|
+
"./src/other.txt",
|
|
202
|
+
dedent/* javascript */ `
|
|
203
|
+
export const HELLO = "WORLD"
|
|
204
|
+
`
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const bundle = await traverseModuleGraph(
|
|
208
|
+
{
|
|
209
|
+
file: path.join(process.cwd(), "./src/index.js"),
|
|
210
|
+
directory: path.join(process.cwd(), "./src"),
|
|
211
|
+
format: "modules",
|
|
212
|
+
// The default module root is dirname(file)
|
|
213
|
+
moduleRoot: path.join(process.cwd(), "./src"),
|
|
214
|
+
},
|
|
215
|
+
[]
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
expect(bundle.modules[0].name).toStrictEqual("other.txt");
|
|
219
|
+
});
|
|
220
|
+
});
|
package/src/api/dev.ts
CHANGED
|
@@ -143,7 +143,6 @@ export async function unstable_dev(
|
|
|
143
143
|
enablePagesAssetsServiceBinding,
|
|
144
144
|
liveReload,
|
|
145
145
|
showInteractiveDevSession,
|
|
146
|
-
forceLocal,
|
|
147
146
|
onReady: (address, port) => {
|
|
148
147
|
readyPort = port;
|
|
149
148
|
readyAddress = address;
|
|
@@ -232,6 +231,7 @@ export async function unstable_dev(
|
|
|
232
231
|
experimentalLocal: experimentalLocal ?? false,
|
|
233
232
|
experimentalLocalRemoteKv: experimentalLocalRemoteKv ?? false,
|
|
234
233
|
enablePagesAssetsServiceBinding,
|
|
234
|
+
forceLocal,
|
|
235
235
|
liveReload,
|
|
236
236
|
onReady: (address, port) => {
|
|
237
237
|
readyPort = port;
|
|
@@ -301,27 +301,16 @@ export async function unstable_dev(
|
|
|
301
301
|
export function parseRequestInput(
|
|
302
302
|
readyAddress: string,
|
|
303
303
|
readyPort: number,
|
|
304
|
-
input
|
|
304
|
+
input: RequestInfo = "/",
|
|
305
305
|
init?: RequestInit,
|
|
306
306
|
protocol: "http" | "https" = "http"
|
|
307
307
|
): [RequestInfo, RequestInit | undefined] {
|
|
308
308
|
if (input instanceof Request) {
|
|
309
309
|
return [input, undefined];
|
|
310
|
-
} else if (input instanceof URL) {
|
|
311
|
-
input = `${protocol}://${readyAddress}:${readyPort}${input.pathname}`;
|
|
312
|
-
} else if (typeof input === "string") {
|
|
313
|
-
try {
|
|
314
|
-
// Want to strip the URL to only get the pathname, but the user could pass in only the pathname
|
|
315
|
-
// Will error if we try and pass "/something" into new URL("/something")
|
|
316
|
-
input = `${protocol}://${readyAddress}:${readyPort}${
|
|
317
|
-
new URL(input).pathname
|
|
318
|
-
}`;
|
|
319
|
-
} catch {
|
|
320
|
-
input = `${protocol}://${readyAddress}:${readyPort}${input}`;
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
input = `${protocol}://${readyAddress}:${readyPort}`;
|
|
324
310
|
}
|
|
325
|
-
|
|
326
|
-
|
|
311
|
+
const url = new URL(`${input}`, `${protocol}://${readyAddress}:${readyPort}`);
|
|
312
|
+
url.protocol = protocol;
|
|
313
|
+
url.hostname = readyAddress;
|
|
314
|
+
url.port = readyPort.toString();
|
|
315
|
+
return [url, init];
|
|
327
316
|
}
|
package/src/bundle.ts
CHANGED
|
@@ -13,6 +13,13 @@ import type { DurableObjectBindings } from "./config/environment";
|
|
|
13
13
|
import type { WorkerRegistry } from "./dev-registry";
|
|
14
14
|
import type { Entry } from "./entry";
|
|
15
15
|
import type { CfModule } from "./worker";
|
|
16
|
+
|
|
17
|
+
export const COMMON_ESBUILD_OPTIONS = {
|
|
18
|
+
// Our workerd runtime uses the same V8 version as recent Chrome, which is highly ES2022 compliant: https://kangax.github.io/compat-table/es2016plus/
|
|
19
|
+
target: "es2022",
|
|
20
|
+
loader: { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx" },
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
16
23
|
export type BundleResult = {
|
|
17
24
|
modules: CfModule[];
|
|
18
25
|
dependencies: esbuild.Metafile["outputs"][string]["inputs"];
|
|
@@ -357,8 +364,7 @@ export async function bundleWorker(
|
|
|
357
364
|
inject,
|
|
358
365
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
359
366
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
360
|
-
|
|
361
|
-
target: "es2022",
|
|
367
|
+
target: COMMON_ESBUILD_OPTIONS.target,
|
|
362
368
|
sourcemap: sourcemap ?? true, // this needs to use ?? to accept false
|
|
363
369
|
// Include a reference to the output folder in the sourcemap.
|
|
364
370
|
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
@@ -377,9 +383,7 @@ export async function bundleWorker(
|
|
|
377
383
|
},
|
|
378
384
|
}),
|
|
379
385
|
loader: {
|
|
380
|
-
|
|
381
|
-
".mjs": "jsx",
|
|
382
|
-
".cjs": "jsx",
|
|
386
|
+
...COMMON_ESBUILD_OPTIONS.loader,
|
|
383
387
|
...(loader || {}),
|
|
384
388
|
},
|
|
385
389
|
plugins: [
|
|
@@ -74,6 +74,12 @@ interface EnvironmentInheritable {
|
|
|
74
74
|
*/
|
|
75
75
|
main: string | undefined;
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* The directory in which module rules should be evaluated in a `--no-bundle` worker
|
|
79
|
+
* This defaults to dirname(main) when left undefined
|
|
80
|
+
*/
|
|
81
|
+
base_dir: string | undefined;
|
|
82
|
+
|
|
77
83
|
/**
|
|
78
84
|
* Whether we use <name>.<subdomain>.workers.dev to
|
|
79
85
|
* test and deploy your worker.
|
|
@@ -339,6 +345,24 @@ interface EnvironmentNonInheritable {
|
|
|
339
345
|
preview_id?: string;
|
|
340
346
|
}[];
|
|
341
347
|
|
|
348
|
+
/**
|
|
349
|
+
* These specify bindings to send email from inside your Worker.
|
|
350
|
+
*
|
|
351
|
+
* NOTE: This field is not automatically inherited from the top level environment,
|
|
352
|
+
* and so must be specified in every named environment.
|
|
353
|
+
*
|
|
354
|
+
* @default `[]`
|
|
355
|
+
* @nonInheritable
|
|
356
|
+
*/
|
|
357
|
+
send_email: {
|
|
358
|
+
/** The binding name used to refer to the this binding */
|
|
359
|
+
name: string;
|
|
360
|
+
/** If this binding should be restricted to a specific verified address */
|
|
361
|
+
destination_address?: string;
|
|
362
|
+
/** If this binding should be restricted to a set of verified addresses */
|
|
363
|
+
allowed_destination_addresses?: string[];
|
|
364
|
+
}[];
|
|
365
|
+
|
|
342
366
|
/**
|
|
343
367
|
* Specifies Queues that are bound to this Worker environment.
|
|
344
368
|
*
|
package/src/config/index.ts
CHANGED
|
@@ -97,6 +97,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
97
97
|
data_blobs,
|
|
98
98
|
durable_objects,
|
|
99
99
|
kv_namespaces,
|
|
100
|
+
send_email,
|
|
100
101
|
queues,
|
|
101
102
|
d1_databases,
|
|
102
103
|
r2_buckets,
|
|
@@ -155,6 +156,23 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
if (send_email !== undefined && send_email.length > 0) {
|
|
160
|
+
output.push({
|
|
161
|
+
type: "Send Email",
|
|
162
|
+
entries: send_email.map(
|
|
163
|
+
({ name, destination_address, allowed_destination_addresses }) => {
|
|
164
|
+
return {
|
|
165
|
+
key: name,
|
|
166
|
+
value:
|
|
167
|
+
destination_address ||
|
|
168
|
+
allowed_destination_addresses?.join(", ") ||
|
|
169
|
+
"unrestricted",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
158
176
|
if (queues !== undefined && queues.length > 0) {
|
|
159
177
|
output.push({
|
|
160
178
|
type: "Queues",
|
package/src/config/validation.ts
CHANGED
|
@@ -358,6 +358,26 @@ function normalizeAndValidateMainField(
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Validate the `base_dir` field and return the normalized values.
|
|
363
|
+
*/
|
|
364
|
+
function normalizeAndValidateBaseDirField(
|
|
365
|
+
configPath: string | undefined,
|
|
366
|
+
rawDir: string | undefined
|
|
367
|
+
): string | undefined {
|
|
368
|
+
const configDir = path.dirname(configPath ?? "wrangler.toml");
|
|
369
|
+
if (rawDir !== undefined) {
|
|
370
|
+
if (typeof rawDir === "string") {
|
|
371
|
+
const directory = path.resolve(configDir);
|
|
372
|
+
return path.resolve(directory, rawDir);
|
|
373
|
+
} else {
|
|
374
|
+
return rawDir;
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
361
381
|
/**
|
|
362
382
|
* Validate the `dev` configuration and return the normalized values.
|
|
363
383
|
*/
|
|
@@ -1011,6 +1031,17 @@ function normalizeAndValidateEnvironment(
|
|
|
1011
1031
|
),
|
|
1012
1032
|
deprecatedUpload
|
|
1013
1033
|
),
|
|
1034
|
+
base_dir: normalizeAndValidateBaseDirField(
|
|
1035
|
+
configPath,
|
|
1036
|
+
inheritable(
|
|
1037
|
+
diagnostics,
|
|
1038
|
+
topLevelEnv,
|
|
1039
|
+
rawEnv,
|
|
1040
|
+
"base_dir",
|
|
1041
|
+
isString,
|
|
1042
|
+
undefined
|
|
1043
|
+
)
|
|
1044
|
+
),
|
|
1014
1045
|
route,
|
|
1015
1046
|
routes,
|
|
1016
1047
|
triggers: inheritable(
|
|
@@ -1074,6 +1105,16 @@ function normalizeAndValidateEnvironment(
|
|
|
1074
1105
|
validateBindingArray(envName, validateKVBinding),
|
|
1075
1106
|
[]
|
|
1076
1107
|
),
|
|
1108
|
+
send_email: notInheritable(
|
|
1109
|
+
diagnostics,
|
|
1110
|
+
topLevelEnv,
|
|
1111
|
+
rawConfig,
|
|
1112
|
+
rawEnv,
|
|
1113
|
+
envName,
|
|
1114
|
+
"send_email",
|
|
1115
|
+
validateBindingArray(envName, validateSendEmailBinding),
|
|
1116
|
+
[]
|
|
1117
|
+
),
|
|
1077
1118
|
queues: notInheritable(
|
|
1078
1119
|
diagnostics,
|
|
1079
1120
|
topLevelEnv,
|
|
@@ -1765,6 +1806,53 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1765
1806
|
return isValid;
|
|
1766
1807
|
};
|
|
1767
1808
|
|
|
1809
|
+
const validateSendEmailBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
1810
|
+
if (typeof value !== "object" || value === null) {
|
|
1811
|
+
diagnostics.errors.push(
|
|
1812
|
+
`"send_email" bindings should be objects, but got ${JSON.stringify(
|
|
1813
|
+
value
|
|
1814
|
+
)}`
|
|
1815
|
+
);
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
let isValid = true;
|
|
1819
|
+
// send email bindings must have a name.
|
|
1820
|
+
if (!isRequiredProperty(value, "name", "string")) {
|
|
1821
|
+
diagnostics.errors.push(
|
|
1822
|
+
`"${field}" bindings should have a string "name" field but got ${JSON.stringify(
|
|
1823
|
+
value
|
|
1824
|
+
)}.`
|
|
1825
|
+
);
|
|
1826
|
+
isValid = false;
|
|
1827
|
+
}
|
|
1828
|
+
if (!isOptionalProperty(value, "destination_address", "string")) {
|
|
1829
|
+
diagnostics.errors.push(
|
|
1830
|
+
`"${field}" bindings should, optionally, have a string "destination_address" field but got ${JSON.stringify(
|
|
1831
|
+
value
|
|
1832
|
+
)}.`
|
|
1833
|
+
);
|
|
1834
|
+
isValid = false;
|
|
1835
|
+
}
|
|
1836
|
+
if (!isOptionalProperty(value, "allowed_destination_addresses", "object")) {
|
|
1837
|
+
diagnostics.errors.push(
|
|
1838
|
+
`"${field}" bindings should, optionally, have a []string "allowed_destination_addresses" field but got ${JSON.stringify(
|
|
1839
|
+
value
|
|
1840
|
+
)}.`
|
|
1841
|
+
);
|
|
1842
|
+
isValid = false;
|
|
1843
|
+
}
|
|
1844
|
+
if (
|
|
1845
|
+
"destination_address" in value &&
|
|
1846
|
+
"allowed_destination_addresses" in value
|
|
1847
|
+
) {
|
|
1848
|
+
diagnostics.errors.push(
|
|
1849
|
+
`"${field}" bindings should have either a "destination_address" or "allowed_destination_addresses" field, but not both.`
|
|
1850
|
+
);
|
|
1851
|
+
isValid = false;
|
|
1852
|
+
}
|
|
1853
|
+
return isValid;
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1768
1856
|
const validateQueueBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
1769
1857
|
if (typeof value !== "object" || value === null) {
|
|
1770
1858
|
diagnostics.errors.push(
|
|
@@ -32,6 +32,12 @@ export type WorkerMetadataBinding =
|
|
|
32
32
|
| { type: "text_blob"; name: string; part: string }
|
|
33
33
|
| { type: "data_blob"; name: string; part: string }
|
|
34
34
|
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
35
|
+
| {
|
|
36
|
+
type: "send_email";
|
|
37
|
+
name: string;
|
|
38
|
+
destination_address?: string;
|
|
39
|
+
allowed_destination_addresses?: string[];
|
|
40
|
+
}
|
|
35
41
|
| {
|
|
36
42
|
type: "durable_object_namespace";
|
|
37
43
|
name: string;
|
|
@@ -105,6 +111,17 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
105
111
|
});
|
|
106
112
|
});
|
|
107
113
|
|
|
114
|
+
bindings.send_email?.forEach(
|
|
115
|
+
({ name, destination_address, allowed_destination_addresses }) => {
|
|
116
|
+
metadataBindings.push({
|
|
117
|
+
name: name,
|
|
118
|
+
type: "send_email",
|
|
119
|
+
destination_address,
|
|
120
|
+
allowed_destination_addresses,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
108
125
|
bindings.durable_objects?.bindings.forEach(
|
|
109
126
|
({ name, class_name, script_name, environment }) => {
|
|
110
127
|
metadataBindings.push({
|
package/src/d1/execute.tsx
CHANGED
|
@@ -37,7 +37,7 @@ export type QueryResult = {
|
|
|
37
37
|
};
|
|
38
38
|
query?: string;
|
|
39
39
|
};
|
|
40
|
-
// Max number of
|
|
40
|
+
// Max number of statements to send in a single /execute call
|
|
41
41
|
const QUERY_LIMIT = 10_000;
|
|
42
42
|
|
|
43
43
|
export function Options(yargs: CommonYargsArgv) {
|
package/src/dev/start-server.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { runCustomBuild } from "../entry";
|
|
15
15
|
import { logger } from "../logger";
|
|
16
16
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
17
|
+
import traverseModuleGraph from "../traverse-module-graph";
|
|
17
18
|
import {
|
|
18
19
|
setupBindings,
|
|
19
20
|
getMiniflare3,
|
|
@@ -253,14 +254,7 @@ async function runEsbuild({
|
|
|
253
254
|
dependencies,
|
|
254
255
|
sourceMapPath,
|
|
255
256
|
}: Awaited<ReturnType<typeof bundleWorker>> = noBundle
|
|
256
|
-
?
|
|
257
|
-
modules: [],
|
|
258
|
-
dependencies: {},
|
|
259
|
-
resolvedEntryPointPath: entry.file,
|
|
260
|
-
bundleType: entry.format === "modules" ? "esm" : "commonjs",
|
|
261
|
-
stop: undefined,
|
|
262
|
-
sourceMapPath: undefined,
|
|
263
|
-
}
|
|
257
|
+
? await traverseModuleGraph(entry, rules)
|
|
264
258
|
: await bundleWorker(entry, destination, {
|
|
265
259
|
serveAssetsFromWorker,
|
|
266
260
|
jsxFactory,
|
package/src/dev/use-esbuild.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { useApp } from "ink";
|
|
|
4
4
|
import { useState, useEffect } from "react";
|
|
5
5
|
import { bundleWorker, rewriteNodeCompatBuildFailure } from "../bundle";
|
|
6
6
|
import { logBuildFailure, logger } from "../logger";
|
|
7
|
+
import traverseModuleGraph from "../traverse-module-graph";
|
|
7
8
|
import type { Config } from "../config";
|
|
8
9
|
import type { WorkerRegistry } from "../dev-registry";
|
|
9
10
|
import type { Entry } from "../entry";
|
|
@@ -107,14 +108,7 @@ export function useEsbuild({
|
|
|
107
108
|
stop,
|
|
108
109
|
sourceMapPath,
|
|
109
110
|
}: Awaited<ReturnType<typeof bundleWorker>> = noBundle
|
|
110
|
-
?
|
|
111
|
-
modules: [],
|
|
112
|
-
dependencies: {},
|
|
113
|
-
resolvedEntryPointPath: entry.file,
|
|
114
|
-
bundleType: entry.format === "modules" ? "esm" : "commonjs",
|
|
115
|
-
stop: undefined,
|
|
116
|
-
sourceMapPath: undefined,
|
|
117
|
-
}
|
|
111
|
+
? await traverseModuleGraph(entry, rules)
|
|
118
112
|
: await bundleWorker(entry, destination, {
|
|
119
113
|
serveAssetsFromWorker,
|
|
120
114
|
jsxFactory,
|
package/src/dev-registry.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { HttpTerminator } from "http-terminator";
|
|
|
13
13
|
const DEV_REGISTRY_PORT = "6284";
|
|
14
14
|
const DEV_REGISTRY_HOST = `http://localhost:${DEV_REGISTRY_PORT}`;
|
|
15
15
|
|
|
16
|
-
let server: Server;
|
|
16
|
+
let server: Server | null;
|
|
17
17
|
let terminator: HttpTerminator;
|
|
18
18
|
|
|
19
19
|
export type WorkerRegistry = Record<string, WorkerDefinition>;
|
|
@@ -102,6 +102,7 @@ export async function startWorkerRegistry() {
|
|
|
102
102
|
*/
|
|
103
103
|
export async function stopWorkerRegistry() {
|
|
104
104
|
await terminator?.terminate();
|
|
105
|
+
server = null;
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
/**
|
package/src/dev.tsx
CHANGED
package/src/entry.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, statSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
5
|
import { execaCommand } from "execa";
|
|
6
|
+
import { COMMON_ESBUILD_OPTIONS } from "./bundle";
|
|
6
7
|
import { logger } from "./logger";
|
|
7
8
|
import { getBasePath } from "./paths";
|
|
8
9
|
import type { Config } from "./config";
|
|
@@ -15,7 +16,16 @@ import type { Metafile } from "esbuild";
|
|
|
15
16
|
*
|
|
16
17
|
* It consists not just of a `file`, but also of a `directory` that is used to resolve relative paths.
|
|
17
18
|
*/
|
|
18
|
-
export type Entry = {
|
|
19
|
+
export type Entry = {
|
|
20
|
+
/** A worker's entrypoint */
|
|
21
|
+
file: string;
|
|
22
|
+
/** A worker's directory. Usually where the wrangler.toml file is located */
|
|
23
|
+
directory: string;
|
|
24
|
+
/** Is this a module worker or a service worker? */
|
|
25
|
+
format: CfScriptFormat;
|
|
26
|
+
/** The directory that contains all of a `--no-bundle` worker's modules. Usually `${directory}/src`. Defaults to path.dirname(file) */
|
|
27
|
+
moduleRoot: string;
|
|
28
|
+
};
|
|
19
29
|
|
|
20
30
|
/**
|
|
21
31
|
* Compute the entry-point for the Worker.
|
|
@@ -99,7 +109,12 @@ export async function getEntry(
|
|
|
99
109
|
);
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
return {
|
|
112
|
+
return {
|
|
113
|
+
file,
|
|
114
|
+
directory,
|
|
115
|
+
format,
|
|
116
|
+
moduleRoot: config.base_dir ?? path.dirname(file),
|
|
117
|
+
};
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
export async function runCustomBuild(
|
|
@@ -147,17 +162,12 @@ export default async function guessWorkerFormat(
|
|
|
147
162
|
tsconfig?: string | undefined
|
|
148
163
|
): Promise<CfScriptFormat> {
|
|
149
164
|
const result = await esbuild.build({
|
|
165
|
+
...COMMON_ESBUILD_OPTIONS,
|
|
150
166
|
entryPoints: [entryFile],
|
|
151
167
|
absWorkingDir: entryWorkingDirectory,
|
|
152
168
|
metafile: true,
|
|
153
169
|
bundle: false,
|
|
154
|
-
target: "es2022",
|
|
155
170
|
write: false,
|
|
156
|
-
loader: {
|
|
157
|
-
".js": "jsx",
|
|
158
|
-
".mjs": "jsx",
|
|
159
|
-
".cjs": "jsx",
|
|
160
|
-
},
|
|
161
171
|
...(tsconfig && { tsconfig }),
|
|
162
172
|
});
|
|
163
173
|
// result.metafile is defined because of the `metafile: true` option above.
|