wrangler 0.0.0-e9a1820 → 0.0.0-e9ab55a
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 +12 -5
- package/pages/functions/buildWorker.ts +62 -0
- package/pages/functions/filepath-routing.test.ts +39 -0
- package/pages/functions/filepath-routing.ts +228 -0
- package/pages/functions/identifiers.ts +78 -0
- package/pages/functions/routes.ts +158 -0
- package/pages/functions/template-worker.ts +136 -0
- package/src/__tests__/dev.test.tsx +67 -0
- package/src/__tests__/jest.setup.ts +5 -1
- package/src/__tests__/kv.test.ts +849 -45
- package/src/__tests__/mock-cfetch.ts +137 -29
- package/src/__tests__/publish.test.ts +102 -0
- package/src/api/form_data.ts +1 -1
- package/src/api/preview.ts +4 -4
- package/src/api/worker.ts +6 -6
- package/src/cfetch/index.ts +102 -0
- package/src/cfetch/internal.ts +69 -0
- package/src/config.ts +633 -118
- package/src/dev.tsx +114 -209
- package/src/index.tsx +265 -239
- package/src/inspect.ts +543 -0
- package/src/kv.tsx +49 -30
- package/src/pages.tsx +431 -242
- package/src/proxy.ts +275 -76
- package/src/publish.ts +79 -47
- package/src/sites.tsx +10 -7
- package/src/tail.tsx +5 -3
- package/src/user.tsx +22 -17
- package/wrangler-dist/cli.js +42319 -36190
- package/wrangler-dist/cli.js.map +3 -3
- package/src/api/inspect.ts +0 -430
- package/src/cfetch.ts +0 -72
package/src/pages.tsx
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/* eslint-disable no-shadow */
|
|
2
2
|
|
|
3
|
-
import assert from "assert";
|
|
4
3
|
import type { BuilderCallback } from "yargs";
|
|
5
4
|
import { join } from "path";
|
|
6
5
|
import { tmpdir } from "os";
|
|
7
|
-
import { existsSync, lstatSync, readFileSync } from "fs";
|
|
6
|
+
import { existsSync, lstatSync, readFileSync, writeFileSync } from "fs";
|
|
8
7
|
import { execSync, spawn } from "child_process";
|
|
9
8
|
import { URL } from "url";
|
|
10
9
|
import { getType } from "mime";
|
|
11
10
|
import open from "open";
|
|
12
11
|
import { watch } from "chokidar";
|
|
12
|
+
import type { BuildResult } from "esbuild";
|
|
13
|
+
import { buildWorker } from "../pages/functions/buildWorker";
|
|
14
|
+
import type { Config } from "../pages/functions/routes";
|
|
15
|
+
import { writeRoutesModule } from "../pages/functions/routes";
|
|
16
|
+
import { generateConfigFromFileTree } from "../pages/functions/filepath-routing";
|
|
13
17
|
|
|
14
18
|
// Defer importing miniflare until we really need it. This takes ~0.5s
|
|
15
19
|
// and also modifies some `stream/web` and `undici` prototypes, so we
|
|
@@ -17,16 +21,28 @@ import { watch } from "chokidar";
|
|
|
17
21
|
import type { Headers, Request, fetch } from "@miniflare/core";
|
|
18
22
|
import type { MiniflareOptions } from "miniflare";
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
const EXIT_CALLBACKS: (() => void)[] = [];
|
|
25
|
+
const EXIT = (message?: string, code?: number) => {
|
|
26
|
+
if (message) console.log(message);
|
|
27
|
+
if (code) process.exitCode = code;
|
|
28
|
+
EXIT_CALLBACKS.forEach((callback) => callback());
|
|
29
|
+
process.exit(code);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
process.on("SIGINT", () => EXIT());
|
|
33
|
+
process.on("SIGTERM", () => EXIT());
|
|
21
34
|
|
|
22
|
-
|
|
35
|
+
function isWindows() {
|
|
36
|
+
return process.platform === "win32";
|
|
37
|
+
}
|
|
23
38
|
|
|
24
39
|
const SECONDS_TO_WAIT_FOR_PROXY = 5;
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
async function sleep(ms: number) {
|
|
27
42
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
|
+
}
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
function getPids(pid: number) {
|
|
30
46
|
const pids: number[] = [pid];
|
|
31
47
|
let command: string, regExp: RegExp;
|
|
32
48
|
|
|
@@ -51,9 +67,9 @@ const getPids = (pid: number) => {
|
|
|
51
67
|
} catch {}
|
|
52
68
|
|
|
53
69
|
return pids;
|
|
54
|
-
}
|
|
70
|
+
}
|
|
55
71
|
|
|
56
|
-
|
|
72
|
+
function getPort(pid: number) {
|
|
57
73
|
let command: string, regExp: RegExp;
|
|
58
74
|
|
|
59
75
|
if (isWindows()) {
|
|
@@ -78,24 +94,19 @@ const getPort = (pid: number) => {
|
|
|
78
94
|
`Error scanning for ports of process with PID ${pid}: ${thrown}`
|
|
79
95
|
);
|
|
80
96
|
}
|
|
81
|
-
}
|
|
97
|
+
}
|
|
82
98
|
|
|
83
|
-
|
|
99
|
+
async function spawnProxyProcess({
|
|
84
100
|
port,
|
|
85
101
|
command,
|
|
86
102
|
}: {
|
|
87
103
|
port?: number;
|
|
88
104
|
command: (string | number)[];
|
|
89
|
-
})
|
|
90
|
-
const exit: Exit = (message) => {
|
|
91
|
-
if (message) console.error(message);
|
|
92
|
-
if (proxy) proxy.kill();
|
|
93
|
-
return undefined;
|
|
94
|
-
};
|
|
95
|
-
|
|
105
|
+
}): Promise<void | number> {
|
|
96
106
|
if (command.length === 0)
|
|
97
|
-
return
|
|
98
|
-
"Must specify a directory of static assets to serve or a command to run."
|
|
107
|
+
return EXIT(
|
|
108
|
+
"Must specify a directory of static assets to serve or a command to run.",
|
|
109
|
+
1
|
|
99
110
|
);
|
|
100
111
|
|
|
101
112
|
console.log(`Running ${command.join(" ")}...`);
|
|
@@ -110,6 +121,9 @@ const spawnProxyProcess = async ({
|
|
|
110
121
|
},
|
|
111
122
|
}
|
|
112
123
|
);
|
|
124
|
+
EXIT_CALLBACKS.push(() => {
|
|
125
|
+
proxy.kill();
|
|
126
|
+
});
|
|
113
127
|
|
|
114
128
|
proxy.stdout.on("data", (data) => {
|
|
115
129
|
console.log(`[proxy]: ${data}`);
|
|
@@ -138,34 +152,35 @@ const spawnProxyProcess = async ({
|
|
|
138
152
|
.filter((port) => port !== undefined)[0];
|
|
139
153
|
|
|
140
154
|
if (port === undefined) {
|
|
141
|
-
return
|
|
142
|
-
"Could not automatically determine proxy port. Please specify the proxy port with --proxy."
|
|
155
|
+
return EXIT(
|
|
156
|
+
"Could not automatically determine proxy port. Please specify the proxy port with --proxy.",
|
|
157
|
+
1
|
|
143
158
|
);
|
|
144
159
|
} else {
|
|
145
160
|
console.log(`Automatically determined the proxy port to be ${port}.`);
|
|
146
161
|
}
|
|
147
162
|
}
|
|
148
163
|
|
|
149
|
-
return
|
|
150
|
-
}
|
|
164
|
+
return port;
|
|
165
|
+
}
|
|
151
166
|
|
|
152
|
-
|
|
167
|
+
function escapeRegex(str: string) {
|
|
153
168
|
return str.replace(/[-/\\^$*+?.()|[]{}]/g, "\\$&");
|
|
154
|
-
}
|
|
169
|
+
}
|
|
155
170
|
|
|
156
|
-
|
|
171
|
+
type Replacements = Record<string, string>;
|
|
157
172
|
|
|
158
|
-
|
|
173
|
+
function replacer(str: string, replacements: Replacements) {
|
|
159
174
|
for (const [replacement, value] of Object.entries(replacements)) {
|
|
160
175
|
str = str.replace(`:${replacement}`, value);
|
|
161
176
|
}
|
|
162
177
|
return str;
|
|
163
|
-
}
|
|
178
|
+
}
|
|
164
179
|
|
|
165
|
-
|
|
180
|
+
function generateRulesMatcher<T>(
|
|
166
181
|
rules?: Record<string, T>,
|
|
167
182
|
replacer: (match: T, replacements: Replacements) => T = (match) => match
|
|
168
|
-
)
|
|
183
|
+
) {
|
|
169
184
|
// TODO: How can you test cross-host rules?
|
|
170
185
|
if (!rules) return () => [];
|
|
171
186
|
|
|
@@ -212,9 +227,9 @@ export const generateRulesMatcher = <T,>(
|
|
|
212
227
|
})
|
|
213
228
|
.filter((value) => value !== undefined) as T[];
|
|
214
229
|
};
|
|
215
|
-
}
|
|
230
|
+
}
|
|
216
231
|
|
|
217
|
-
|
|
232
|
+
function generateHeadersMatcher(headersFile: string) {
|
|
218
233
|
if (existsSync(headersFile)) {
|
|
219
234
|
const contents = readFileSync(headersFile).toString();
|
|
220
235
|
|
|
@@ -281,9 +296,9 @@ const generateHeadersMatcher = (headersFile: string) => {
|
|
|
281
296
|
} else {
|
|
282
297
|
return () => undefined;
|
|
283
298
|
}
|
|
284
|
-
}
|
|
299
|
+
}
|
|
285
300
|
|
|
286
|
-
|
|
301
|
+
function generateRedirectsMatcher(redirectsFile: string) {
|
|
287
302
|
if (existsSync(redirectsFile)) {
|
|
288
303
|
const contents = readFileSync(redirectsFile).toString();
|
|
289
304
|
|
|
@@ -330,26 +345,26 @@ const generateRedirectsMatcher = (redirectsFile: string) => {
|
|
|
330
345
|
} else {
|
|
331
346
|
return () => undefined;
|
|
332
347
|
}
|
|
333
|
-
}
|
|
348
|
+
}
|
|
334
349
|
|
|
335
|
-
|
|
350
|
+
function extractPathname(
|
|
336
351
|
path = "/",
|
|
337
352
|
includeSearch: boolean,
|
|
338
353
|
includeHash: boolean
|
|
339
|
-
)
|
|
354
|
+
) {
|
|
340
355
|
if (!path.startsWith("/")) path = `/${path}`;
|
|
341
356
|
const url = new URL(`//${path}`, "relative://");
|
|
342
357
|
return `${url.pathname}${includeSearch ? url.search : ""}${
|
|
343
358
|
includeHash ? url.hash : ""
|
|
344
359
|
}`;
|
|
345
|
-
}
|
|
360
|
+
}
|
|
346
361
|
|
|
347
|
-
|
|
362
|
+
function validateURL(
|
|
348
363
|
token: string,
|
|
349
364
|
onlyRelative = false,
|
|
350
365
|
includeSearch = false,
|
|
351
366
|
includeHash = false
|
|
352
|
-
)
|
|
367
|
+
) {
|
|
353
368
|
const host = /^https:\/\/+(?<host>[^/]+)\/?(?<path>.*)/.exec(token);
|
|
354
369
|
if (host && host.groups && host.groups.host) {
|
|
355
370
|
if (onlyRelative) return;
|
|
@@ -370,14 +385,13 @@ const validateURL = (
|
|
|
370
385
|
}
|
|
371
386
|
}
|
|
372
387
|
return "";
|
|
373
|
-
}
|
|
388
|
+
}
|
|
374
389
|
|
|
375
|
-
|
|
376
|
-
/\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
390
|
+
function hasFileExtension(pathname: string) {
|
|
391
|
+
return /\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
392
|
+
}
|
|
377
393
|
|
|
378
|
-
|
|
379
|
-
directory: string
|
|
380
|
-
): Promise<typeof fetch> => {
|
|
394
|
+
async function generateAssetsFetch(directory: string): Promise<typeof fetch> {
|
|
381
395
|
// Defer importing miniflare until we really need it
|
|
382
396
|
const { Headers, Request, Response } = await import("@miniflare/core");
|
|
383
397
|
|
|
@@ -623,229 +637,404 @@ const generateAssetsFetch = async (
|
|
|
623
637
|
status: deconstructedResponse.status,
|
|
624
638
|
});
|
|
625
639
|
};
|
|
626
|
-
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const RUNNING_BUILDERS: BuildResult[] = [];
|
|
643
|
+
|
|
644
|
+
async function buildFunctions({
|
|
645
|
+
scriptPath,
|
|
646
|
+
outputConfigPath,
|
|
647
|
+
functionsDirectory,
|
|
648
|
+
minify = false,
|
|
649
|
+
sourcemap = false,
|
|
650
|
+
fallbackService = "ASSETS",
|
|
651
|
+
watch = false,
|
|
652
|
+
onEnd,
|
|
653
|
+
}: {
|
|
654
|
+
scriptPath: string;
|
|
655
|
+
outputConfigPath?: string;
|
|
656
|
+
functionsDirectory: string;
|
|
657
|
+
minify?: boolean;
|
|
658
|
+
sourcemap?: boolean;
|
|
659
|
+
fallbackService?: string;
|
|
660
|
+
watch?: boolean;
|
|
661
|
+
onEnd?: () => void;
|
|
662
|
+
}) {
|
|
663
|
+
RUNNING_BUILDERS.forEach(
|
|
664
|
+
(runningBuilder) => runningBuilder.stop && runningBuilder.stop()
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
const routesModule = join(tmpdir(), "./functionsRoutes.mjs");
|
|
668
|
+
|
|
669
|
+
const config: Config = await generateConfigFromFileTree({
|
|
670
|
+
baseDir: functionsDirectory,
|
|
671
|
+
baseURL: "/",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
if (outputConfigPath) {
|
|
675
|
+
writeFileSync(
|
|
676
|
+
outputConfigPath,
|
|
677
|
+
JSON.stringify({ ...config, baseURL: "/" }, null, 2)
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
await writeRoutesModule({
|
|
682
|
+
config,
|
|
683
|
+
srcDir: functionsDirectory,
|
|
684
|
+
outfile: routesModule,
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
RUNNING_BUILDERS.push(
|
|
688
|
+
await buildWorker({
|
|
689
|
+
routesModule,
|
|
690
|
+
outfile: scriptPath,
|
|
691
|
+
minify,
|
|
692
|
+
sourcemap,
|
|
693
|
+
fallbackService,
|
|
694
|
+
watch,
|
|
695
|
+
onEnd,
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
}
|
|
627
699
|
|
|
628
700
|
export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
629
|
-
return yargs
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
demandOption: undefined,
|
|
637
|
-
description: "The directory of static assets to serve",
|
|
638
|
-
})
|
|
639
|
-
.positional("command", {
|
|
640
|
-
type: "string",
|
|
641
|
-
demandOption: undefined,
|
|
642
|
-
description: "The proxy command to run",
|
|
643
|
-
})
|
|
644
|
-
.options({
|
|
645
|
-
local: {
|
|
646
|
-
type: "boolean",
|
|
647
|
-
default: true,
|
|
648
|
-
description: "Run on my machine",
|
|
649
|
-
},
|
|
650
|
-
port: {
|
|
651
|
-
type: "number",
|
|
652
|
-
default: 8788,
|
|
653
|
-
description: "The port to listen on (serve from)",
|
|
654
|
-
},
|
|
655
|
-
proxy: {
|
|
656
|
-
type: "number",
|
|
657
|
-
description:
|
|
658
|
-
"The port to proxy (where the static assets are served)",
|
|
659
|
-
},
|
|
660
|
-
"script-path": {
|
|
701
|
+
return yargs
|
|
702
|
+
.command(
|
|
703
|
+
"dev [directory] [-- command]",
|
|
704
|
+
"🧑💻 Develop your full-stack Pages application locally",
|
|
705
|
+
(yargs) => {
|
|
706
|
+
return yargs
|
|
707
|
+
.positional("directory", {
|
|
661
708
|
type: "string",
|
|
662
|
-
|
|
663
|
-
description:
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
description: "
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
709
|
+
demandOption: undefined,
|
|
710
|
+
description: "The directory of static assets to serve",
|
|
711
|
+
})
|
|
712
|
+
.positional("command", {
|
|
713
|
+
type: "string",
|
|
714
|
+
demandOption: undefined,
|
|
715
|
+
description: "The proxy command to run",
|
|
716
|
+
})
|
|
717
|
+
.options({
|
|
718
|
+
local: {
|
|
719
|
+
type: "boolean",
|
|
720
|
+
default: true,
|
|
721
|
+
description: "Run on my machine",
|
|
722
|
+
},
|
|
723
|
+
port: {
|
|
724
|
+
type: "number",
|
|
725
|
+
default: 8788,
|
|
726
|
+
description: "The port to listen on (serve from)",
|
|
727
|
+
},
|
|
728
|
+
proxy: {
|
|
729
|
+
type: "number",
|
|
730
|
+
description:
|
|
731
|
+
"The port to proxy (where the static assets are served)",
|
|
732
|
+
},
|
|
733
|
+
"script-path": {
|
|
734
|
+
type: "string",
|
|
735
|
+
default: "_worker.js",
|
|
736
|
+
description:
|
|
737
|
+
"The location of the single Worker script if not using functions",
|
|
738
|
+
},
|
|
739
|
+
binding: {
|
|
740
|
+
type: "array",
|
|
741
|
+
description: "Bind variable/secret (KEY=VALUE)",
|
|
742
|
+
alias: "b",
|
|
743
|
+
},
|
|
744
|
+
kv: {
|
|
745
|
+
type: "array",
|
|
746
|
+
description: "KV namespace to bind",
|
|
747
|
+
alias: "k",
|
|
748
|
+
},
|
|
749
|
+
do: {
|
|
750
|
+
type: "array",
|
|
751
|
+
description: "Durable Object to bind (NAME=CLASS)",
|
|
752
|
+
alias: "o",
|
|
753
|
+
},
|
|
754
|
+
"live-reload": {
|
|
755
|
+
type: "boolean",
|
|
756
|
+
default: false,
|
|
757
|
+
description: "Auto reload HTML pages when change is detected",
|
|
758
|
+
},
|
|
759
|
+
// TODO: Miniflare user options
|
|
760
|
+
});
|
|
761
|
+
},
|
|
762
|
+
async ({
|
|
763
|
+
local,
|
|
764
|
+
directory,
|
|
765
|
+
port,
|
|
766
|
+
proxy: requestedProxyPort,
|
|
767
|
+
"script-path": singleWorkerScriptPath,
|
|
768
|
+
binding: bindings = [],
|
|
769
|
+
kv: kvs = [],
|
|
770
|
+
do: durableObjects = [],
|
|
771
|
+
"live-reload": liveReload,
|
|
772
|
+
"--": remaining = [],
|
|
773
|
+
}) => {
|
|
774
|
+
if (!local) {
|
|
775
|
+
console.error("Only local mode is supported at the moment.");
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
699
778
|
|
|
700
|
-
|
|
701
|
-
|
|
779
|
+
const functionsDirectory = "./functions";
|
|
780
|
+
const usingFunctions = existsSync(functionsDirectory);
|
|
702
781
|
|
|
703
|
-
|
|
782
|
+
const command = remaining as (string | number)[];
|
|
704
783
|
|
|
705
|
-
|
|
706
|
-
let exit: Exit = (message) => {
|
|
707
|
-
console.error(message);
|
|
708
|
-
return undefined;
|
|
709
|
-
};
|
|
784
|
+
let proxyPort: number | void;
|
|
710
785
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
786
|
+
if (directory === undefined) {
|
|
787
|
+
proxyPort = await spawnProxyProcess({
|
|
788
|
+
port: requestedProxyPort,
|
|
789
|
+
command,
|
|
790
|
+
});
|
|
791
|
+
if (proxyPort === undefined) return undefined;
|
|
792
|
+
}
|
|
717
793
|
|
|
718
|
-
|
|
719
|
-
proxyPort = proxy.port;
|
|
794
|
+
let miniflareArgs: MiniflareOptions = {};
|
|
720
795
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
796
|
+
let scriptReadyResolve;
|
|
797
|
+
const scriptReadyPromise = new Promise(
|
|
798
|
+
(resolve) => (scriptReadyResolve = resolve)
|
|
799
|
+
);
|
|
724
800
|
|
|
725
|
-
|
|
801
|
+
if (usingFunctions) {
|
|
802
|
+
const scriptPath = join(tmpdir(), "./functionsWorker.js");
|
|
726
803
|
|
|
727
|
-
|
|
728
|
-
const scriptPath = join(tmpdir(), "./functionsWorker.js");
|
|
729
|
-
miniflareArgs = {
|
|
730
|
-
scriptPath,
|
|
731
|
-
buildWatchPaths: [functionsDirectory],
|
|
732
|
-
buildCommand: `npx @cloudflare/pages-functions-compiler build ${functionsDirectory} --outfile ${scriptPath}`,
|
|
733
|
-
};
|
|
734
|
-
} else {
|
|
735
|
-
const scriptPath =
|
|
736
|
-
directory !== undefined
|
|
737
|
-
? join(directory, singleWorkerScriptPath)
|
|
738
|
-
: singleWorkerScriptPath;
|
|
804
|
+
console.log(`Compiling worker to "${scriptPath}"...`);
|
|
739
805
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
806
|
+
await buildFunctions({
|
|
807
|
+
scriptPath,
|
|
808
|
+
functionsDirectory,
|
|
809
|
+
sourcemap: true,
|
|
810
|
+
watch: true,
|
|
811
|
+
onEnd: () => scriptReadyResolve(),
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
watch([functionsDirectory], {
|
|
815
|
+
persistent: true,
|
|
816
|
+
ignoreInitial: true,
|
|
817
|
+
}).on("all", async () => {
|
|
818
|
+
await buildFunctions({
|
|
819
|
+
scriptPath,
|
|
820
|
+
functionsDirectory,
|
|
821
|
+
sourcemap: true,
|
|
822
|
+
watch: true,
|
|
823
|
+
onEnd: () => scriptReadyResolve(),
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
miniflareArgs = {
|
|
828
|
+
scriptPath,
|
|
829
|
+
};
|
|
830
|
+
} else {
|
|
831
|
+
const scriptPath =
|
|
832
|
+
directory !== undefined
|
|
833
|
+
? join(directory, singleWorkerScriptPath)
|
|
834
|
+
: singleWorkerScriptPath;
|
|
835
|
+
|
|
836
|
+
if (existsSync(scriptPath)) {
|
|
837
|
+
miniflareArgs = {
|
|
838
|
+
scriptPath,
|
|
839
|
+
};
|
|
840
|
+
} else {
|
|
841
|
+
console.log("No functions. Shimming...");
|
|
842
|
+
miniflareArgs = {
|
|
843
|
+
// TODO: The fact that these request/response hacks are necessary is ridiculous.
|
|
844
|
+
// We need to eliminate them from env.ASSETS.fetch (not sure if just local or prod as well)
|
|
845
|
+
script: `
|
|
846
|
+
export default {
|
|
847
|
+
async fetch(request, env, context) {
|
|
848
|
+
const response = await env.ASSETS.fetch(request.url, request)
|
|
849
|
+
return new Response(response.body, response)
|
|
850
|
+
}
|
|
851
|
+
}`,
|
|
852
|
+
};
|
|
853
|
+
}
|
|
744
854
|
}
|
|
745
855
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
};
|
|
749
|
-
}
|
|
856
|
+
// Defer importing miniflare until we really need it
|
|
857
|
+
const { Miniflare, Log, LogLevel } = await import("miniflare");
|
|
858
|
+
const { Response, fetch } = await import("@miniflare/core");
|
|
750
859
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
860
|
+
// Wait for esbuild to finish building before starting Miniflare.
|
|
861
|
+
// This must be before the call to `new Miniflare`, as that will
|
|
862
|
+
// asynchronously start loading the script. `await startServer()`
|
|
863
|
+
// internally just waits for that promise to resolve.
|
|
864
|
+
await scriptReadyPromise;
|
|
754
865
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
port,
|
|
762
|
-
watch: true,
|
|
763
|
-
modules: true,
|
|
866
|
+
// `assetsFetch()` will only be called if there is `proxyPort` defined.
|
|
867
|
+
// We only define `proxyPort`, above, when there is no `directory` defined.
|
|
868
|
+
const assetsFetch =
|
|
869
|
+
directory !== undefined
|
|
870
|
+
? await generateAssetsFetch(directory)
|
|
871
|
+
: invalidAssetsFetch;
|
|
764
872
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
873
|
+
const miniflare = new Miniflare({
|
|
874
|
+
port,
|
|
875
|
+
watch: true,
|
|
876
|
+
modules: true,
|
|
768
877
|
|
|
769
|
-
|
|
878
|
+
log: new Log(LogLevel.ERROR, { prefix: "pages" }),
|
|
879
|
+
logUnhandledRejections: true,
|
|
880
|
+
sourceMap: true,
|
|
770
881
|
|
|
771
|
-
|
|
772
|
-
durableObjects.map((durableObject) =>
|
|
773
|
-
durableObject.toString().split("=")
|
|
774
|
-
)
|
|
775
|
-
),
|
|
882
|
+
kvNamespaces: kvs.map((kv) => kv.toString()),
|
|
776
883
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
884
|
+
durableObjects: Object.fromEntries(
|
|
885
|
+
durableObjects.map((durableObject) =>
|
|
886
|
+
durableObject.toString().split("=")
|
|
887
|
+
)
|
|
781
888
|
),
|
|
782
|
-
},
|
|
783
|
-
|
|
784
|
-
// env.ASSETS.fetch
|
|
785
|
-
serviceBindings: {
|
|
786
|
-
async ASSETS(request) {
|
|
787
|
-
if (proxyPort) {
|
|
788
|
-
try {
|
|
789
|
-
const url = new URL(request.url);
|
|
790
|
-
url.host = `127.0.0.1:${proxyPort}`;
|
|
791
|
-
return await fetch(url, request);
|
|
792
|
-
} catch (thrown) {
|
|
793
|
-
console.error(`Could not proxy request: ${thrown}`);
|
|
794
|
-
|
|
795
|
-
// TODO: Pretty error page
|
|
796
|
-
return new Response(
|
|
797
|
-
`[wrangler] Could not proxy request: ${thrown}`,
|
|
798
|
-
{ status: 502 }
|
|
799
|
-
);
|
|
800
|
-
}
|
|
801
|
-
} else {
|
|
802
|
-
try {
|
|
803
|
-
return await assetsFetch(request);
|
|
804
|
-
} catch (thrown) {
|
|
805
|
-
console.error(`Could not serve static asset: ${thrown}`);
|
|
806
|
-
|
|
807
|
-
// TODO: Pretty error page
|
|
808
|
-
return new Response(
|
|
809
|
-
`[wrangler] Could not serve static asset: ${thrown}`,
|
|
810
|
-
{ status: 502 }
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
},
|
|
815
|
-
},
|
|
816
889
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
890
|
+
// User bindings
|
|
891
|
+
bindings: {
|
|
892
|
+
...Object.fromEntries(
|
|
893
|
+
bindings.map((binding) => binding.toString().split("="))
|
|
894
|
+
),
|
|
895
|
+
},
|
|
823
896
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
897
|
+
// env.ASSETS.fetch
|
|
898
|
+
serviceBindings: {
|
|
899
|
+
async ASSETS(request) {
|
|
900
|
+
if (proxyPort) {
|
|
901
|
+
try {
|
|
902
|
+
const url = new URL(request.url);
|
|
903
|
+
url.host = `localhost:${proxyPort}`;
|
|
904
|
+
return await fetch(url, request);
|
|
905
|
+
} catch (thrown) {
|
|
906
|
+
console.error(`Could not proxy request: ${thrown}`);
|
|
907
|
+
|
|
908
|
+
// TODO: Pretty error page
|
|
909
|
+
return new Response(
|
|
910
|
+
`[wrangler] Could not proxy request: ${thrown}`,
|
|
911
|
+
{ status: 502 }
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
try {
|
|
916
|
+
return await assetsFetch(request);
|
|
917
|
+
} catch (thrown) {
|
|
918
|
+
console.error(`Could not serve static asset: ${thrown}`);
|
|
919
|
+
|
|
920
|
+
// TODO: Pretty error page
|
|
921
|
+
return new Response(
|
|
922
|
+
`[wrangler] Could not serve static asset: ${thrown}`,
|
|
923
|
+
{ status: 502 }
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
},
|
|
828
929
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
930
|
+
kvPersist: true,
|
|
931
|
+
durableObjectsPersist: true,
|
|
932
|
+
cachePersist: true,
|
|
933
|
+
liveReload,
|
|
832
934
|
|
|
833
|
-
|
|
834
|
-
server.close();
|
|
835
|
-
miniflare.dispose().catch((err) => {
|
|
836
|
-
miniflare.log.error(err);
|
|
837
|
-
});
|
|
935
|
+
...miniflareArgs,
|
|
838
936
|
});
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
937
|
+
|
|
938
|
+
try {
|
|
939
|
+
// `startServer` might throw if user code contains errors
|
|
940
|
+
const server = await miniflare.startServer();
|
|
941
|
+
console.log(`Serving at http://localhost:${port}/`);
|
|
942
|
+
|
|
943
|
+
if (process.env.BROWSER !== "none") {
|
|
944
|
+
const childProcess = await open(`http://localhost:${port}/`);
|
|
945
|
+
// fail silently if the open command doesn't work (e.g. in GitHub Codespaces)
|
|
946
|
+
childProcess.on("error", (_err) => {});
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (directory !== undefined && liveReload) {
|
|
950
|
+
watch([directory], {
|
|
951
|
+
persistent: true,
|
|
952
|
+
ignoreInitial: true,
|
|
953
|
+
}).on("all", async () => {
|
|
954
|
+
await miniflare.reload();
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
EXIT_CALLBACKS.push(() => {
|
|
959
|
+
server.close();
|
|
960
|
+
miniflare.dispose().catch((err) => miniflare.log.error(err));
|
|
843
961
|
});
|
|
844
|
-
})
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
962
|
+
} catch (e) {
|
|
963
|
+
miniflare.log.error(e);
|
|
964
|
+
EXIT("Could not start Miniflare.", 1);
|
|
965
|
+
}
|
|
848
966
|
}
|
|
849
|
-
|
|
967
|
+
)
|
|
968
|
+
.command("functions", "Cloudflare Pages Functions", (yargs) =>
|
|
969
|
+
yargs.command(
|
|
970
|
+
"build [directory]",
|
|
971
|
+
"Compile a folder of Cloudflare Pages Functions into a single Worker",
|
|
972
|
+
(yargs) =>
|
|
973
|
+
yargs
|
|
974
|
+
.positional("directory", {
|
|
975
|
+
type: "string",
|
|
976
|
+
default: "functions",
|
|
977
|
+
description: "The directory of Pages Functions",
|
|
978
|
+
})
|
|
979
|
+
.options({
|
|
980
|
+
"script-path": {
|
|
981
|
+
type: "string",
|
|
982
|
+
default: "_worker.js",
|
|
983
|
+
description: "The location of the output Worker script",
|
|
984
|
+
},
|
|
985
|
+
"output-config-path": {
|
|
986
|
+
type: "string",
|
|
987
|
+
description: "The location for the output config file",
|
|
988
|
+
},
|
|
989
|
+
minify: {
|
|
990
|
+
type: "boolean",
|
|
991
|
+
default: false,
|
|
992
|
+
description: "Minify the output Worker script",
|
|
993
|
+
},
|
|
994
|
+
sourcemap: {
|
|
995
|
+
type: "boolean",
|
|
996
|
+
default: false,
|
|
997
|
+
description:
|
|
998
|
+
"Generate a sourcemap for the output Worker script",
|
|
999
|
+
},
|
|
1000
|
+
"fallback-service": {
|
|
1001
|
+
type: "string",
|
|
1002
|
+
default: "ASSETS",
|
|
1003
|
+
description:
|
|
1004
|
+
"The service to fallback to at the end of the `next` chain. Setting to '' will fallback to the global `fetch`.",
|
|
1005
|
+
},
|
|
1006
|
+
watch: {
|
|
1007
|
+
type: "boolean",
|
|
1008
|
+
default: false,
|
|
1009
|
+
description:
|
|
1010
|
+
"Watch for changes to the functions and automatically rebuild the Worker script",
|
|
1011
|
+
},
|
|
1012
|
+
}),
|
|
1013
|
+
async ({
|
|
1014
|
+
directory,
|
|
1015
|
+
"script-path": scriptPath,
|
|
1016
|
+
"output-config-path": outputConfigPath,
|
|
1017
|
+
minify,
|
|
1018
|
+
sourcemap,
|
|
1019
|
+
fallbackService,
|
|
1020
|
+
watch,
|
|
1021
|
+
}) => {
|
|
1022
|
+
await buildFunctions({
|
|
1023
|
+
scriptPath,
|
|
1024
|
+
outputConfigPath,
|
|
1025
|
+
functionsDirectory: directory,
|
|
1026
|
+
minify,
|
|
1027
|
+
sourcemap,
|
|
1028
|
+
fallbackService,
|
|
1029
|
+
watch,
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
)
|
|
1033
|
+
);
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const invalidAssetsFetch: typeof fetch = () => {
|
|
1037
|
+
throw new Error(
|
|
1038
|
+
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
|
|
850
1039
|
);
|
|
851
1040
|
};
|