wrangler 0.0.0-e928f94 → 0.0.0-e95bea2
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/bin/wrangler.js +2 -2
- package/package.json +46 -18
- package/pages/functions/buildWorker.ts +62 -0
- package/pages/functions/filepath-routing.test.ts +123 -0
- package/pages/functions/filepath-routing.ts +178 -0
- package/pages/functions/identifiers.ts +78 -0
- package/pages/functions/routes.ts +151 -0
- package/pages/functions/template-worker.ts +130 -0
- package/src/__tests__/configuration.test.ts +2217 -0
- package/src/__tests__/dev.test.tsx +405 -0
- package/src/__tests__/dev2.test.tsx +152 -0
- package/src/__tests__/guess-worker-format.test.ts +58 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +44 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/helpers/mock-cfetch.ts +207 -0
- package/src/__tests__/helpers/mock-console.ts +70 -0
- package/src/__tests__/helpers/mock-dialogs.ts +65 -0
- package/src/__tests__/helpers/mock-kv.ts +40 -0
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/mock-user.ts +27 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +29 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/https-options.test.ts +127 -0
- package/src/__tests__/index.test.ts +957 -119
- package/src/__tests__/jest.setup.ts +69 -0
- package/src/__tests__/kv.test.ts +1408 -0
- package/src/__tests__/logout.test.ts +50 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/pages.test.ts +44 -0
- package/src/__tests__/parse.test.ts +322 -0
- package/src/__tests__/publish.test.ts +2453 -0
- package/src/__tests__/r2.test.ts +198 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +425 -0
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +564 -0
- package/src/__tests__/whoami.test.tsx +86 -0
- package/src/bundle.ts +142 -0
- package/src/cfetch/index.ts +104 -0
- package/src/cfetch/internal.ts +135 -0
- package/src/cli.ts +16 -7
- package/src/config/README.md +107 -0
- package/src/config/config.ts +279 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +305 -0
- package/src/config/index.ts +52 -0
- package/src/config/validation-helpers.ts +388 -0
- package/src/config/validation.ts +1027 -0
- package/src/{api/preview.ts → create-worker-preview.ts} +63 -25
- package/src/create-worker-upload-form.ts +217 -0
- package/src/dev/dev.tsx +357 -0
- package/src/dev/local.tsx +241 -0
- package/src/dev/remote.tsx +201 -0
- package/src/dev/use-esbuild.ts +100 -0
- package/src/dialogs.tsx +5 -13
- package/src/entry.ts +159 -0
- package/src/environment-variables.ts +33 -0
- package/src/https-options.ts +122 -0
- package/src/index.tsx +1374 -903
- package/src/inspect.ts +617 -0
- package/src/kv/index.tsx +339 -0
- package/src/module-collection.ts +215 -26
- package/src/open-in-browser.ts +25 -0
- package/src/package-manager.ts +126 -0
- package/src/pages.tsx +452 -250
- package/src/parse.ts +183 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +438 -0
- package/src/publish.ts +300 -292
- package/src/r2.ts +50 -0
- package/src/reporting.ts +131 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +181 -58
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +62 -0
- package/src/user.tsx +166 -130
- package/src/whoami.tsx +69 -0
- package/src/worker.ts +163 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +131958 -115793
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/fixtures/init/.gitkeep +0 -0
- package/src/__tests__/mock-cfetch.js +0 -46
- package/src/api/form_data.ts +0 -141
- package/src/api/inspect.ts +0 -441
- package/src/api/worker.ts +0 -161
- package/src/cfetch.ts +0 -72
- package/src/config.ts +0 -122
- package/src/dev.tsx +0 -724
- package/src/kv.tsx +0 -211
- package/src/tail.tsx +0 -71
package/src/pages.tsx
CHANGED
|
@@ -1,24 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import { existsSync, lstatSync, readFileSync } from "fs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import type { RequestInfo, RequestInit } from "@miniflare/core";
|
|
9
|
-
import open from "open";
|
|
1
|
+
/* eslint-disable no-shadow */
|
|
2
|
+
|
|
3
|
+
import { execSync, spawn } from "node:child_process";
|
|
4
|
+
import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { URL } from "node:url";
|
|
10
8
|
import { watch } from "chokidar";
|
|
9
|
+
import { getType } from "mime";
|
|
10
|
+
import { buildWorker } from "../pages/functions/buildWorker";
|
|
11
|
+
import { generateConfigFromFileTree } from "../pages/functions/filepath-routing";
|
|
12
|
+
import { writeRoutesModule } from "../pages/functions/routes";
|
|
13
|
+
import openInBrowser from "./open-in-browser";
|
|
14
|
+
import { toUrlPath } from "./paths";
|
|
15
|
+
import type { Config } from "../pages/functions/routes";
|
|
16
|
+
import type { Headers, Request, fetch } from "@miniflare/core";
|
|
17
|
+
import type { BuildResult } from "esbuild";
|
|
18
|
+
import type { MiniflareOptions } from "miniflare";
|
|
19
|
+
import type { BuilderCallback } from "yargs";
|
|
20
|
+
|
|
21
|
+
// Defer importing miniflare until we really need it. This takes ~0.5s
|
|
22
|
+
// and also modifies some `stream/web` and `undici` prototypes, so we
|
|
23
|
+
// don't want to do this if pages commands aren't being called.
|
|
24
|
+
|
|
25
|
+
const EXIT_CALLBACKS: (() => void)[] = [];
|
|
26
|
+
const EXIT = (message?: string, code?: number) => {
|
|
27
|
+
if (message) console.log(message);
|
|
28
|
+
if (code) process.exitCode = code;
|
|
29
|
+
EXIT_CALLBACKS.forEach((callback) => callback());
|
|
30
|
+
process.exit(code);
|
|
31
|
+
};
|
|
11
32
|
|
|
12
|
-
|
|
33
|
+
process.on("SIGINT", () => EXIT());
|
|
34
|
+
process.on("SIGTERM", () => EXIT());
|
|
13
35
|
|
|
14
|
-
|
|
36
|
+
function isWindows() {
|
|
37
|
+
return process.platform === "win32";
|
|
38
|
+
}
|
|
15
39
|
|
|
16
40
|
const SECONDS_TO_WAIT_FOR_PROXY = 5;
|
|
17
41
|
|
|
18
|
-
|
|
42
|
+
async function sleep(ms: number) {
|
|
19
43
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
44
|
+
}
|
|
20
45
|
|
|
21
|
-
|
|
46
|
+
function getPids(pid: number) {
|
|
22
47
|
const pids: number[] = [pid];
|
|
23
48
|
let command: string, regExp: RegExp;
|
|
24
49
|
|
|
@@ -43,9 +68,9 @@ const getPids = (pid: number) => {
|
|
|
43
68
|
} catch {}
|
|
44
69
|
|
|
45
70
|
return pids;
|
|
46
|
-
}
|
|
71
|
+
}
|
|
47
72
|
|
|
48
|
-
|
|
73
|
+
function getPort(pid: number) {
|
|
49
74
|
let command: string, regExp: RegExp;
|
|
50
75
|
|
|
51
76
|
if (isWindows()) {
|
|
@@ -70,24 +95,19 @@ const getPort = (pid: number) => {
|
|
|
70
95
|
`Error scanning for ports of process with PID ${pid}: ${thrown}`
|
|
71
96
|
);
|
|
72
97
|
}
|
|
73
|
-
}
|
|
98
|
+
}
|
|
74
99
|
|
|
75
|
-
|
|
100
|
+
async function spawnProxyProcess({
|
|
76
101
|
port,
|
|
77
102
|
command,
|
|
78
103
|
}: {
|
|
79
104
|
port?: number;
|
|
80
105
|
command: (string | number)[];
|
|
81
|
-
})
|
|
82
|
-
const exit: Exit = (message) => {
|
|
83
|
-
if (message) console.error(message);
|
|
84
|
-
if (proxy) proxy.kill();
|
|
85
|
-
return undefined;
|
|
86
|
-
};
|
|
87
|
-
|
|
106
|
+
}): Promise<void | number> {
|
|
88
107
|
if (command.length === 0)
|
|
89
|
-
return
|
|
90
|
-
"Must specify a directory of static assets to serve or a command to run."
|
|
108
|
+
return EXIT(
|
|
109
|
+
"Must specify a directory of static assets to serve or a command to run.",
|
|
110
|
+
1
|
|
91
111
|
);
|
|
92
112
|
|
|
93
113
|
console.log(`Running ${command.join(" ")}...`);
|
|
@@ -102,6 +122,9 @@ const spawnProxyProcess = async ({
|
|
|
102
122
|
},
|
|
103
123
|
}
|
|
104
124
|
);
|
|
125
|
+
EXIT_CALLBACKS.push(() => {
|
|
126
|
+
proxy.kill();
|
|
127
|
+
});
|
|
105
128
|
|
|
106
129
|
proxy.stdout.on("data", (data) => {
|
|
107
130
|
console.log(`[proxy]: ${data}`);
|
|
@@ -130,34 +153,35 @@ const spawnProxyProcess = async ({
|
|
|
130
153
|
.filter((port) => port !== undefined)[0];
|
|
131
154
|
|
|
132
155
|
if (port === undefined) {
|
|
133
|
-
return
|
|
134
|
-
"Could not automatically determine proxy port. Please specify the proxy port with --proxy."
|
|
156
|
+
return EXIT(
|
|
157
|
+
"Could not automatically determine proxy port. Please specify the proxy port with --proxy.",
|
|
158
|
+
1
|
|
135
159
|
);
|
|
136
160
|
} else {
|
|
137
161
|
console.log(`Automatically determined the proxy port to be ${port}.`);
|
|
138
162
|
}
|
|
139
163
|
}
|
|
140
164
|
|
|
141
|
-
return
|
|
142
|
-
}
|
|
165
|
+
return port;
|
|
166
|
+
}
|
|
143
167
|
|
|
144
|
-
|
|
168
|
+
function escapeRegex(str: string) {
|
|
145
169
|
return str.replace(/[-/\\^$*+?.()|[]{}]/g, "\\$&");
|
|
146
|
-
}
|
|
170
|
+
}
|
|
147
171
|
|
|
148
|
-
|
|
172
|
+
type Replacements = Record<string, string>;
|
|
149
173
|
|
|
150
|
-
|
|
174
|
+
function replacer(str: string, replacements: Replacements) {
|
|
151
175
|
for (const [replacement, value] of Object.entries(replacements)) {
|
|
152
176
|
str = str.replace(`:${replacement}`, value);
|
|
153
177
|
}
|
|
154
178
|
return str;
|
|
155
|
-
}
|
|
179
|
+
}
|
|
156
180
|
|
|
157
|
-
|
|
181
|
+
function generateRulesMatcher<T>(
|
|
158
182
|
rules?: Record<string, T>,
|
|
159
183
|
replacer: (match: T, replacements: Replacements) => T = (match) => match
|
|
160
|
-
)
|
|
184
|
+
) {
|
|
161
185
|
// TODO: How can you test cross-host rules?
|
|
162
186
|
if (!rules) return () => [];
|
|
163
187
|
|
|
@@ -204,9 +228,9 @@ export const generateRulesMatcher = <T,>(
|
|
|
204
228
|
})
|
|
205
229
|
.filter((value) => value !== undefined) as T[];
|
|
206
230
|
};
|
|
207
|
-
}
|
|
231
|
+
}
|
|
208
232
|
|
|
209
|
-
|
|
233
|
+
function generateHeadersMatcher(headersFile: string) {
|
|
210
234
|
if (existsSync(headersFile)) {
|
|
211
235
|
const contents = readFileSync(headersFile).toString();
|
|
212
236
|
|
|
@@ -273,9 +297,9 @@ const generateHeadersMatcher = (headersFile: string) => {
|
|
|
273
297
|
} else {
|
|
274
298
|
return () => undefined;
|
|
275
299
|
}
|
|
276
|
-
}
|
|
300
|
+
}
|
|
277
301
|
|
|
278
|
-
|
|
302
|
+
function generateRedirectsMatcher(redirectsFile: string) {
|
|
279
303
|
if (existsSync(redirectsFile)) {
|
|
280
304
|
const contents = readFileSync(redirectsFile).toString();
|
|
281
305
|
|
|
@@ -322,26 +346,26 @@ const generateRedirectsMatcher = (redirectsFile: string) => {
|
|
|
322
346
|
} else {
|
|
323
347
|
return () => undefined;
|
|
324
348
|
}
|
|
325
|
-
}
|
|
349
|
+
}
|
|
326
350
|
|
|
327
|
-
|
|
351
|
+
function extractPathname(
|
|
328
352
|
path = "/",
|
|
329
353
|
includeSearch: boolean,
|
|
330
354
|
includeHash: boolean
|
|
331
|
-
)
|
|
355
|
+
) {
|
|
332
356
|
if (!path.startsWith("/")) path = `/${path}`;
|
|
333
357
|
const url = new URL(`//${path}`, "relative://");
|
|
334
358
|
return `${url.pathname}${includeSearch ? url.search : ""}${
|
|
335
359
|
includeHash ? url.hash : ""
|
|
336
360
|
}`;
|
|
337
|
-
}
|
|
361
|
+
}
|
|
338
362
|
|
|
339
|
-
|
|
363
|
+
function validateURL(
|
|
340
364
|
token: string,
|
|
341
365
|
onlyRelative = false,
|
|
342
366
|
includeSearch = false,
|
|
343
367
|
includeHash = false
|
|
344
|
-
)
|
|
368
|
+
) {
|
|
345
369
|
const host = /^https:\/\/+(?<host>[^/]+)\/?(?<path>.*)/.exec(token);
|
|
346
370
|
if (host && host.groups && host.groups.host) {
|
|
347
371
|
if (onlyRelative) return;
|
|
@@ -362,14 +386,16 @@ const validateURL = (
|
|
|
362
386
|
}
|
|
363
387
|
}
|
|
364
388
|
return "";
|
|
365
|
-
}
|
|
389
|
+
}
|
|
366
390
|
|
|
367
|
-
|
|
368
|
-
/\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
391
|
+
function hasFileExtension(pathname: string) {
|
|
392
|
+
return /\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function generateAssetsFetch(directory: string): Promise<typeof fetch> {
|
|
396
|
+
// Defer importing miniflare until we really need it
|
|
397
|
+
const { Headers, Request, Response } = await import("@miniflare/core");
|
|
369
398
|
|
|
370
|
-
const generateAssetsFetch = async (
|
|
371
|
-
directory: string
|
|
372
|
-
): Promise<typeof fetch> => {
|
|
373
399
|
const headersFile = join(directory, "_headers");
|
|
374
400
|
const redirectsFile = join(directory, "_redirects");
|
|
375
401
|
const workerFile = join(directory, "_worker.js");
|
|
@@ -466,14 +492,25 @@ const generateAssetsFetch = async (
|
|
|
466
492
|
if ((asset = getAsset(`${cwd}/404.html`))) {
|
|
467
493
|
deconstructedResponse.status = 404;
|
|
468
494
|
deconstructedResponse.body = serveAsset(asset);
|
|
495
|
+
deconstructedResponse.headers.set(
|
|
496
|
+
"Content-Type",
|
|
497
|
+
getType(asset) || "application/octet-stream"
|
|
498
|
+
);
|
|
469
499
|
return deconstructedResponse;
|
|
470
500
|
}
|
|
471
501
|
}
|
|
472
502
|
|
|
473
503
|
if ((asset = getAsset(`/index.html`))) {
|
|
474
504
|
deconstructedResponse.body = serveAsset(asset);
|
|
505
|
+
deconstructedResponse.headers.set(
|
|
506
|
+
"Content-Type",
|
|
507
|
+
getType(asset) || "application/octet-stream"
|
|
508
|
+
);
|
|
475
509
|
return deconstructedResponse;
|
|
476
510
|
}
|
|
511
|
+
|
|
512
|
+
deconstructedResponse.status = 404;
|
|
513
|
+
return deconstructedResponse;
|
|
477
514
|
};
|
|
478
515
|
|
|
479
516
|
let asset;
|
|
@@ -481,6 +518,10 @@ const generateAssetsFetch = async (
|
|
|
481
518
|
if (url.pathname.endsWith("/")) {
|
|
482
519
|
if ((asset = getAsset(`${url.pathname}/index.html`))) {
|
|
483
520
|
deconstructedResponse.body = serveAsset(asset);
|
|
521
|
+
deconstructedResponse.headers.set(
|
|
522
|
+
"Content-Type",
|
|
523
|
+
getType(asset) || "application/octet-stream"
|
|
524
|
+
);
|
|
484
525
|
return deconstructedResponse;
|
|
485
526
|
} else if (
|
|
486
527
|
(asset = getAsset(`${url.pathname.replace(/\/$/, ".html")}`))
|
|
@@ -508,6 +549,10 @@ const generateAssetsFetch = async (
|
|
|
508
549
|
const extensionlessPath = url.pathname.slice(0, -".html".length);
|
|
509
550
|
if (getAsset(extensionlessPath) || extensionlessPath === "/") {
|
|
510
551
|
deconstructedResponse.body = serveAsset(asset);
|
|
552
|
+
deconstructedResponse.headers.set(
|
|
553
|
+
"Content-Type",
|
|
554
|
+
getType(asset) || "application/octet-stream"
|
|
555
|
+
);
|
|
511
556
|
return deconstructedResponse;
|
|
512
557
|
} else {
|
|
513
558
|
deconstructedResponse.status = 301;
|
|
@@ -519,6 +564,10 @@ const generateAssetsFetch = async (
|
|
|
519
564
|
}
|
|
520
565
|
} else {
|
|
521
566
|
deconstructedResponse.body = serveAsset(asset);
|
|
567
|
+
deconstructedResponse.headers.set(
|
|
568
|
+
"Content-Type",
|
|
569
|
+
getType(asset) || "application/octet-stream"
|
|
570
|
+
);
|
|
522
571
|
return deconstructedResponse;
|
|
523
572
|
}
|
|
524
573
|
} else if (hasFileExtension(url.pathname)) {
|
|
@@ -528,6 +577,10 @@ const generateAssetsFetch = async (
|
|
|
528
577
|
|
|
529
578
|
if ((asset = getAsset(`${url.pathname}.html`))) {
|
|
530
579
|
deconstructedResponse.body = serveAsset(asset);
|
|
580
|
+
deconstructedResponse.headers.set(
|
|
581
|
+
"Content-Type",
|
|
582
|
+
getType(asset) || "application/octet-stream"
|
|
583
|
+
);
|
|
531
584
|
return deconstructedResponse;
|
|
532
585
|
}
|
|
533
586
|
|
|
@@ -569,7 +622,7 @@ const generateAssetsFetch = async (
|
|
|
569
622
|
});
|
|
570
623
|
};
|
|
571
624
|
|
|
572
|
-
return
|
|
625
|
+
return async (input, init) => {
|
|
573
626
|
const request = new Request(input, init);
|
|
574
627
|
const deconstructedResponse = generateResponse(request);
|
|
575
628
|
attachHeaders(request, deconstructedResponse);
|
|
@@ -584,210 +637,276 @@ const generateAssetsFetch = async (
|
|
|
584
637
|
headers,
|
|
585
638
|
status: deconstructedResponse.status,
|
|
586
639
|
});
|
|
587
|
-
}
|
|
588
|
-
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const RUNNING_BUILDERS: BuildResult[] = [];
|
|
644
|
+
|
|
645
|
+
async function buildFunctions({
|
|
646
|
+
scriptPath,
|
|
647
|
+
outputConfigPath,
|
|
648
|
+
functionsDirectory,
|
|
649
|
+
minify = false,
|
|
650
|
+
sourcemap = false,
|
|
651
|
+
fallbackService = "ASSETS",
|
|
652
|
+
watch = false,
|
|
653
|
+
onEnd,
|
|
654
|
+
}: {
|
|
655
|
+
scriptPath: string;
|
|
656
|
+
outputConfigPath?: string;
|
|
657
|
+
functionsDirectory: string;
|
|
658
|
+
minify?: boolean;
|
|
659
|
+
sourcemap?: boolean;
|
|
660
|
+
fallbackService?: string;
|
|
661
|
+
watch?: boolean;
|
|
662
|
+
onEnd?: () => void;
|
|
663
|
+
}) {
|
|
664
|
+
RUNNING_BUILDERS.forEach(
|
|
665
|
+
(runningBuilder) => runningBuilder.stop && runningBuilder.stop()
|
|
666
|
+
);
|
|
589
667
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
"dev [directory] [-- command]",
|
|
593
|
-
"🧑💻 Develop your full-stack Pages application locally",
|
|
594
|
-
(yargs) => {
|
|
595
|
-
return yargs
|
|
596
|
-
.positional("directory", {
|
|
597
|
-
type: "string",
|
|
598
|
-
demandOption: undefined,
|
|
599
|
-
description: "The directory of static assets to serve",
|
|
600
|
-
})
|
|
601
|
-
.positional("command", {
|
|
602
|
-
type: "string",
|
|
603
|
-
demandOption: undefined,
|
|
604
|
-
description: "The proxy command to run",
|
|
605
|
-
})
|
|
606
|
-
.options({
|
|
607
|
-
local: {
|
|
608
|
-
type: "boolean",
|
|
609
|
-
default: true,
|
|
610
|
-
description: "Run on my machine",
|
|
611
|
-
},
|
|
612
|
-
port: {
|
|
613
|
-
type: "number",
|
|
614
|
-
default: 8788,
|
|
615
|
-
description: "The port to listen on (serve from)",
|
|
616
|
-
},
|
|
617
|
-
proxy: {
|
|
618
|
-
type: "number",
|
|
619
|
-
description:
|
|
620
|
-
"The port to proxy (where the static assets are served)",
|
|
621
|
-
},
|
|
622
|
-
"script-path": {
|
|
623
|
-
type: "string",
|
|
624
|
-
default: "_worker.js",
|
|
625
|
-
description:
|
|
626
|
-
"The location of the single Worker script if not using functions",
|
|
627
|
-
},
|
|
628
|
-
binding: {
|
|
629
|
-
type: "array",
|
|
630
|
-
description: "Bind variable/secret (KEY=VALUE)",
|
|
631
|
-
alias: "b",
|
|
632
|
-
},
|
|
633
|
-
kv: {
|
|
634
|
-
type: "array",
|
|
635
|
-
description: "KV namespace to bind",
|
|
636
|
-
alias: "k",
|
|
637
|
-
},
|
|
638
|
-
do: {
|
|
639
|
-
type: "array",
|
|
640
|
-
description: "Durable Object to bind (NAME=CLASS)",
|
|
641
|
-
alias: "o",
|
|
642
|
-
},
|
|
643
|
-
// TODO: Miniflare user options
|
|
644
|
-
});
|
|
645
|
-
},
|
|
646
|
-
async ({
|
|
647
|
-
local,
|
|
648
|
-
directory,
|
|
649
|
-
port,
|
|
650
|
-
proxy: requestedProxyPort,
|
|
651
|
-
"script-path": singleWorkerScriptPath,
|
|
652
|
-
binding: bindings = [],
|
|
653
|
-
kv: kvs = [],
|
|
654
|
-
do: durableObjects = [],
|
|
655
|
-
"--": remaining = [],
|
|
656
|
-
}) => {
|
|
657
|
-
if (!local) {
|
|
658
|
-
console.error("Only local mode is supported at the moment.");
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
668
|
+
const routesModule = join(tmpdir(), "./functionsRoutes.mjs");
|
|
669
|
+
const baseURL = toUrlPath("/");
|
|
661
670
|
|
|
662
|
-
|
|
663
|
-
|
|
671
|
+
const config: Config = await generateConfigFromFileTree({
|
|
672
|
+
baseDir: functionsDirectory,
|
|
673
|
+
baseURL,
|
|
674
|
+
});
|
|
664
675
|
|
|
665
|
-
|
|
676
|
+
if (outputConfigPath) {
|
|
677
|
+
writeFileSync(
|
|
678
|
+
outputConfigPath,
|
|
679
|
+
JSON.stringify({ ...config, baseURL }, null, 2)
|
|
680
|
+
);
|
|
681
|
+
}
|
|
666
682
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
683
|
+
await writeRoutesModule({
|
|
684
|
+
config,
|
|
685
|
+
srcDir: functionsDirectory,
|
|
686
|
+
outfile: routesModule,
|
|
687
|
+
});
|
|
672
688
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
689
|
+
RUNNING_BUILDERS.push(
|
|
690
|
+
await buildWorker({
|
|
691
|
+
routesModule,
|
|
692
|
+
outfile: scriptPath,
|
|
693
|
+
minify,
|
|
694
|
+
sourcemap,
|
|
695
|
+
fallbackService,
|
|
696
|
+
watch,
|
|
697
|
+
onEnd,
|
|
698
|
+
})
|
|
699
|
+
);
|
|
700
|
+
}
|
|
679
701
|
|
|
680
|
-
|
|
681
|
-
|
|
702
|
+
export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
703
|
+
return yargs
|
|
704
|
+
.command(
|
|
705
|
+
"dev [directory] [-- command]",
|
|
706
|
+
"🧑💻 Develop your full-stack Pages application locally",
|
|
707
|
+
(yargs) => {
|
|
708
|
+
return yargs
|
|
709
|
+
.positional("directory", {
|
|
710
|
+
type: "string",
|
|
711
|
+
demandOption: undefined,
|
|
712
|
+
description: "The directory of static assets to serve",
|
|
713
|
+
})
|
|
714
|
+
.positional("command", {
|
|
715
|
+
type: "string",
|
|
716
|
+
demandOption: undefined,
|
|
717
|
+
description: "The proxy command to run",
|
|
718
|
+
})
|
|
719
|
+
.options({
|
|
720
|
+
local: {
|
|
721
|
+
type: "boolean",
|
|
722
|
+
default: true,
|
|
723
|
+
description: "Run on my machine",
|
|
724
|
+
},
|
|
725
|
+
port: {
|
|
726
|
+
type: "number",
|
|
727
|
+
default: 8788,
|
|
728
|
+
description: "The port to listen on (serve from)",
|
|
729
|
+
},
|
|
730
|
+
proxy: {
|
|
731
|
+
type: "number",
|
|
732
|
+
description:
|
|
733
|
+
"The port to proxy (where the static assets are served)",
|
|
734
|
+
},
|
|
735
|
+
"script-path": {
|
|
736
|
+
type: "string",
|
|
737
|
+
default: "_worker.js",
|
|
738
|
+
description:
|
|
739
|
+
"The location of the single Worker script if not using functions",
|
|
740
|
+
},
|
|
741
|
+
binding: {
|
|
742
|
+
type: "array",
|
|
743
|
+
description: "Bind variable/secret (KEY=VALUE)",
|
|
744
|
+
alias: "b",
|
|
745
|
+
},
|
|
746
|
+
kv: {
|
|
747
|
+
type: "array",
|
|
748
|
+
description: "KV namespace to bind",
|
|
749
|
+
alias: "k",
|
|
750
|
+
},
|
|
751
|
+
do: {
|
|
752
|
+
type: "array",
|
|
753
|
+
description: "Durable Object to bind (NAME=CLASS)",
|
|
754
|
+
alias: "o",
|
|
755
|
+
},
|
|
756
|
+
"live-reload": {
|
|
757
|
+
type: "boolean",
|
|
758
|
+
default: false,
|
|
759
|
+
description: "Auto reload HTML pages when change is detected",
|
|
760
|
+
},
|
|
761
|
+
// TODO: Miniflare user options
|
|
762
|
+
});
|
|
763
|
+
},
|
|
764
|
+
async ({
|
|
765
|
+
local,
|
|
766
|
+
directory,
|
|
767
|
+
port,
|
|
768
|
+
proxy: requestedProxyPort,
|
|
769
|
+
"script-path": singleWorkerScriptPath,
|
|
770
|
+
binding: bindings = [],
|
|
771
|
+
kv: kvs = [],
|
|
772
|
+
do: durableObjects = [],
|
|
773
|
+
"live-reload": liveReload,
|
|
774
|
+
_: [_pages, _dev, ...remaining],
|
|
775
|
+
}) => {
|
|
776
|
+
if (!local) {
|
|
777
|
+
console.error("Only local mode is supported at the moment.");
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
682
780
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
781
|
+
const functionsDirectory = "./functions";
|
|
782
|
+
const usingFunctions = existsSync(functionsDirectory);
|
|
686
783
|
|
|
687
|
-
|
|
784
|
+
const command = remaining as (string | number)[];
|
|
688
785
|
|
|
689
|
-
|
|
690
|
-
const scriptPath = join(tmpdir(), "./functionsWorker.js");
|
|
691
|
-
miniflareArgs = {
|
|
692
|
-
scriptPath,
|
|
693
|
-
buildWatchPaths: [functionsDirectory],
|
|
694
|
-
buildCommand: `npx @cloudflare/pages-functions-compiler build ${functionsDirectory} --outfile ${scriptPath}`,
|
|
695
|
-
};
|
|
696
|
-
} else {
|
|
697
|
-
const scriptPath =
|
|
698
|
-
directory !== undefined
|
|
699
|
-
? join(directory, singleWorkerScriptPath)
|
|
700
|
-
: singleWorkerScriptPath;
|
|
786
|
+
let proxyPort: number | void;
|
|
701
787
|
|
|
702
|
-
if (
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
788
|
+
if (directory === undefined) {
|
|
789
|
+
proxyPort = await spawnProxyProcess({
|
|
790
|
+
port: requestedProxyPort,
|
|
791
|
+
command,
|
|
792
|
+
});
|
|
793
|
+
if (proxyPort === undefined) return undefined;
|
|
706
794
|
}
|
|
707
795
|
|
|
708
|
-
miniflareArgs = {
|
|
709
|
-
scriptPath,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
796
|
+
let miniflareArgs: MiniflareOptions = {};
|
|
712
797
|
|
|
713
|
-
|
|
714
|
-
|
|
798
|
+
let scriptReadyResolve: () => void;
|
|
799
|
+
const scriptReadyPromise = new Promise<void>(
|
|
800
|
+
(resolve) => (scriptReadyResolve = resolve)
|
|
801
|
+
);
|
|
715
802
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
803
|
+
if (usingFunctions) {
|
|
804
|
+
const scriptPath = join(tmpdir(), "./functionsWorker.js");
|
|
805
|
+
|
|
806
|
+
console.log(`Compiling worker to "${scriptPath}"...`);
|
|
807
|
+
|
|
808
|
+
await buildFunctions({
|
|
809
|
+
scriptPath,
|
|
810
|
+
functionsDirectory,
|
|
811
|
+
sourcemap: true,
|
|
812
|
+
watch: true,
|
|
813
|
+
onEnd: () => scriptReadyResolve(),
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
watch([functionsDirectory], {
|
|
817
|
+
persistent: true,
|
|
818
|
+
ignoreInitial: true,
|
|
819
|
+
}).on("all", async () => {
|
|
820
|
+
await buildFunctions({
|
|
821
|
+
scriptPath,
|
|
822
|
+
functionsDirectory,
|
|
823
|
+
sourcemap: true,
|
|
824
|
+
watch: true,
|
|
825
|
+
onEnd: () => scriptReadyResolve(),
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
miniflareArgs = {
|
|
830
|
+
scriptPath,
|
|
831
|
+
};
|
|
832
|
+
} else {
|
|
833
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
834
|
+
scriptReadyResolve!();
|
|
835
|
+
|
|
836
|
+
const scriptPath =
|
|
837
|
+
directory !== undefined
|
|
838
|
+
? join(directory, singleWorkerScriptPath)
|
|
839
|
+
: singleWorkerScriptPath;
|
|
840
|
+
|
|
841
|
+
if (existsSync(scriptPath)) {
|
|
842
|
+
miniflareArgs = {
|
|
843
|
+
scriptPath,
|
|
844
|
+
};
|
|
845
|
+
} else {
|
|
846
|
+
console.log("No functions. Shimming...");
|
|
847
|
+
miniflareArgs = {
|
|
848
|
+
// TODO: The fact that these request/response hacks are necessary is ridiculous.
|
|
849
|
+
// We need to eliminate them from env.ASSETS.fetch (not sure if just local or prod as well)
|
|
850
|
+
script: `
|
|
851
|
+
export default {
|
|
852
|
+
async fetch(request, env, context) {
|
|
853
|
+
const response = await env.ASSETS.fetch(request.url, request)
|
|
854
|
+
return new Response(response.body, response)
|
|
742
855
|
}
|
|
743
|
-
}
|
|
856
|
+
}`,
|
|
857
|
+
};
|
|
744
858
|
}
|
|
745
859
|
}
|
|
746
860
|
|
|
747
|
-
|
|
861
|
+
// Defer importing miniflare until we really need it
|
|
862
|
+
const { Miniflare, Log, LogLevel } = await import("miniflare");
|
|
863
|
+
const { Response, fetch } = await import("@miniflare/core");
|
|
748
864
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
}
|
|
865
|
+
// Wait for esbuild to finish building before starting Miniflare.
|
|
866
|
+
// This must be before the call to `new Miniflare`, as that will
|
|
867
|
+
// asynchronously start loading the script. `await startServer()`
|
|
868
|
+
// internally just waits for that promise to resolve.
|
|
869
|
+
await scriptReadyPromise;
|
|
756
870
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
871
|
+
// `assetsFetch()` will only be called if there is `proxyPort` defined.
|
|
872
|
+
// We only define `proxyPort`, above, when there is no `directory` defined.
|
|
873
|
+
const assetsFetch =
|
|
874
|
+
directory !== undefined
|
|
875
|
+
? await generateAssetsFetch(directory)
|
|
876
|
+
: invalidAssetsFetch;
|
|
761
877
|
|
|
762
|
-
|
|
878
|
+
const miniflare = new Miniflare({
|
|
879
|
+
port,
|
|
880
|
+
watch: true,
|
|
881
|
+
modules: true,
|
|
763
882
|
|
|
764
|
-
|
|
883
|
+
log: new Log(LogLevel.ERROR, { prefix: "pages" }),
|
|
884
|
+
logUnhandledRejections: true,
|
|
885
|
+
sourceMap: true,
|
|
765
886
|
|
|
766
|
-
|
|
767
|
-
durableObjects.map((durableObject) =>
|
|
768
|
-
durableObject.toString().split("=")
|
|
769
|
-
)
|
|
770
|
-
),
|
|
887
|
+
kvNamespaces: kvs.map((kv) => kv.toString()),
|
|
771
888
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
889
|
+
durableObjects: Object.fromEntries(
|
|
890
|
+
durableObjects.map((durableObject) =>
|
|
891
|
+
durableObject.toString().split("=")
|
|
892
|
+
)
|
|
776
893
|
),
|
|
777
894
|
|
|
895
|
+
// User bindings
|
|
896
|
+
bindings: {
|
|
897
|
+
...Object.fromEntries(
|
|
898
|
+
bindings.map((binding) => binding.toString().split("=", 1))
|
|
899
|
+
),
|
|
900
|
+
},
|
|
901
|
+
|
|
778
902
|
// env.ASSETS.fetch
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
input: RequestInfo,
|
|
782
|
-
init?: RequestInit | undefined
|
|
783
|
-
) => {
|
|
903
|
+
serviceBindings: {
|
|
904
|
+
async ASSETS(request: Request) {
|
|
784
905
|
if (proxyPort) {
|
|
785
906
|
try {
|
|
786
|
-
let request = new Request(input as any, init);
|
|
787
907
|
const url = new URL(request.url);
|
|
788
|
-
url.host = `
|
|
789
|
-
|
|
790
|
-
return await fetch(request);
|
|
908
|
+
url.host = `localhost:${proxyPort}`;
|
|
909
|
+
return await fetch(url, request);
|
|
791
910
|
} catch (thrown) {
|
|
792
911
|
console.error(`Could not proxy request: ${thrown}`);
|
|
793
912
|
|
|
@@ -799,9 +918,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
799
918
|
}
|
|
800
919
|
} else {
|
|
801
920
|
try {
|
|
802
|
-
return await (
|
|
803
|
-
await generateAssetsFetch(directory)
|
|
804
|
-
)(input as any, init as any);
|
|
921
|
+
return await assetsFetch(request);
|
|
805
922
|
} catch (thrown) {
|
|
806
923
|
console.error(`Could not serve static asset: ${thrown}`);
|
|
807
924
|
|
|
@@ -814,30 +931,115 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
|
814
931
|
}
|
|
815
932
|
},
|
|
816
933
|
},
|
|
817
|
-
},
|
|
818
934
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
935
|
+
kvPersist: true,
|
|
936
|
+
durableObjectsPersist: true,
|
|
937
|
+
cachePersist: true,
|
|
938
|
+
liveReload,
|
|
822
939
|
|
|
823
|
-
|
|
824
|
-
|
|
940
|
+
...miniflareArgs,
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
try {
|
|
944
|
+
// `startServer` might throw if user code contains errors
|
|
945
|
+
const server = await miniflare.startServer();
|
|
946
|
+
console.log(`Serving at http://localhost:${port}/`);
|
|
825
947
|
|
|
826
|
-
|
|
827
|
-
|
|
948
|
+
if (process.env.BROWSER !== "none") {
|
|
949
|
+
await openInBrowser(`http://localhost:${port}/`);
|
|
950
|
+
}
|
|
828
951
|
|
|
829
|
-
|
|
830
|
-
|
|
952
|
+
if (directory !== undefined && liveReload) {
|
|
953
|
+
watch([directory], {
|
|
954
|
+
persistent: true,
|
|
955
|
+
ignoreInitial: true,
|
|
956
|
+
}).on("all", async () => {
|
|
957
|
+
await miniflare.reload();
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
EXIT_CALLBACKS.push(() => {
|
|
962
|
+
server.close();
|
|
963
|
+
miniflare.dispose().catch((err) => miniflare.log.error(err));
|
|
964
|
+
});
|
|
965
|
+
} catch (e) {
|
|
966
|
+
miniflare.log.error(e as Error);
|
|
967
|
+
EXIT("Could not start Miniflare.", 1);
|
|
968
|
+
}
|
|
831
969
|
}
|
|
970
|
+
)
|
|
971
|
+
.command("functions", false, (yargs) =>
|
|
972
|
+
// we hide this command from help output because
|
|
973
|
+
// it's not meant to be used directly right now
|
|
974
|
+
yargs.command(
|
|
975
|
+
"build [directory]",
|
|
976
|
+
"Compile a folder of Cloudflare Pages Functions into a single Worker",
|
|
977
|
+
(yargs) =>
|
|
978
|
+
yargs
|
|
979
|
+
.positional("directory", {
|
|
980
|
+
type: "string",
|
|
981
|
+
default: "functions",
|
|
982
|
+
description: "The directory of Pages Functions",
|
|
983
|
+
})
|
|
984
|
+
.options({
|
|
985
|
+
"script-path": {
|
|
986
|
+
type: "string",
|
|
987
|
+
default: "_worker.js",
|
|
988
|
+
description: "The location of the output Worker script",
|
|
989
|
+
},
|
|
990
|
+
"output-config-path": {
|
|
991
|
+
type: "string",
|
|
992
|
+
description: "The location for the output config file",
|
|
993
|
+
},
|
|
994
|
+
minify: {
|
|
995
|
+
type: "boolean",
|
|
996
|
+
default: false,
|
|
997
|
+
description: "Minify the output Worker script",
|
|
998
|
+
},
|
|
999
|
+
sourcemap: {
|
|
1000
|
+
type: "boolean",
|
|
1001
|
+
default: false,
|
|
1002
|
+
description:
|
|
1003
|
+
"Generate a sourcemap for the output Worker script",
|
|
1004
|
+
},
|
|
1005
|
+
"fallback-service": {
|
|
1006
|
+
type: "string",
|
|
1007
|
+
default: "ASSETS",
|
|
1008
|
+
description:
|
|
1009
|
+
"The service to fallback to at the end of the `next` chain. Setting to '' will fallback to the global `fetch`.",
|
|
1010
|
+
},
|
|
1011
|
+
watch: {
|
|
1012
|
+
type: "boolean",
|
|
1013
|
+
default: false,
|
|
1014
|
+
description:
|
|
1015
|
+
"Watch for changes to the functions and automatically rebuild the Worker script",
|
|
1016
|
+
},
|
|
1017
|
+
}),
|
|
1018
|
+
async ({
|
|
1019
|
+
directory,
|
|
1020
|
+
"script-path": scriptPath,
|
|
1021
|
+
"output-config-path": outputConfigPath,
|
|
1022
|
+
minify,
|
|
1023
|
+
sourcemap,
|
|
1024
|
+
fallbackService,
|
|
1025
|
+
watch,
|
|
1026
|
+
}) => {
|
|
1027
|
+
await buildFunctions({
|
|
1028
|
+
scriptPath,
|
|
1029
|
+
outputConfigPath,
|
|
1030
|
+
functionsDirectory: directory,
|
|
1031
|
+
minify,
|
|
1032
|
+
sourcemap,
|
|
1033
|
+
fallbackService,
|
|
1034
|
+
watch,
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
)
|
|
1038
|
+
);
|
|
1039
|
+
};
|
|
832
1040
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
});
|
|
837
|
-
process.on("SIGTERM", () => {
|
|
838
|
-
server.close();
|
|
839
|
-
miniflare.dispose();
|
|
840
|
-
});
|
|
841
|
-
}
|
|
1041
|
+
const invalidAssetsFetch: typeof fetch = () => {
|
|
1042
|
+
throw new Error(
|
|
1043
|
+
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
|
|
842
1044
|
);
|
|
843
1045
|
};
|