wrangler 2.0.25 → 2.0.26
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/miniflare-dist/index.mjs +15 -3
- package/package.json +3 -3
- package/src/__tests__/helpers/mock-cfetch.ts +33 -0
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +3 -0
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/tail.test.ts +19 -3
- package/src/api/dev.ts +1 -0
- package/src/cfetch/internal.ts +39 -0
- package/src/dev/dev.tsx +4 -0
- package/src/dev.tsx +2 -2
- package/src/init.ts +111 -38
- package/src/miniflare-cli/assets.ts +8 -0
- package/src/miniflare-cli/index.ts +6 -3
- package/src/pages/build.tsx +41 -15
- package/src/pages/constants.ts +1 -0
- package/src/pages/dev.tsx +86 -24
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/routes-consolidation.test.ts +185 -1
- package/src/pages/functions/routes-consolidation.ts +46 -2
- package/src/pages/functions/routes-transformation.ts +0 -3
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/publish.tsx +27 -16
- package/src/tail/filters.ts +3 -1
- package/src/tail/printing.ts +2 -0
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/service-bindings-module-facade.js +10 -7
- package/templates/service-bindings-sw-facade.js +10 -7
- package/wrangler-dist/cli.d.ts +1 -0
- package/wrangler-dist/cli.js +1034 -738
package/src/pages/index.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Build from "./build";
|
|
4
4
|
import * as Deployments from "./deployments";
|
|
5
5
|
import * as Dev from "./dev";
|
|
6
|
+
import * as Functions from "./functions";
|
|
6
7
|
import * as Projects from "./projects";
|
|
7
8
|
import * as Publish from "./publish";
|
|
8
9
|
import * as Upload from "./upload";
|
|
@@ -19,66 +20,75 @@ process.on("SIGTERM", () => {
|
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"create [project-name]",
|
|
51
|
-
"Create a new Cloudflare Pages project",
|
|
52
|
-
Projects.CreateOptions,
|
|
53
|
-
Projects.CreateHandler
|
|
54
|
-
)
|
|
55
|
-
.command("upload [directory]", false, Upload.Options, Upload.Handler)
|
|
56
|
-
.epilogue(pagesBetaWarning)
|
|
57
|
-
)
|
|
58
|
-
.command(
|
|
59
|
-
"deployment",
|
|
60
|
-
"🚀 Interact with the deployments of a project",
|
|
61
|
-
(yargs) =>
|
|
23
|
+
return (
|
|
24
|
+
yargs
|
|
25
|
+
.command(
|
|
26
|
+
"dev [directory] [-- command..]",
|
|
27
|
+
"🧑💻 Develop your full-stack Pages application locally",
|
|
28
|
+
Dev.Options,
|
|
29
|
+
Dev.Handler
|
|
30
|
+
)
|
|
31
|
+
/**
|
|
32
|
+
* `wrangler pages functions` is meant for internal use only for now,
|
|
33
|
+
* so let's hide this command from the help output
|
|
34
|
+
*/
|
|
35
|
+
.command("functions", false, (yargs) =>
|
|
36
|
+
yargs
|
|
37
|
+
.command(
|
|
38
|
+
"build [directory]",
|
|
39
|
+
"Compile a folder of Cloudflare Pages Functions into a single Worker",
|
|
40
|
+
Build.Options,
|
|
41
|
+
Build.Handler
|
|
42
|
+
)
|
|
43
|
+
.command(
|
|
44
|
+
"optimize-routes [routesPath] [outputRoutesPath]",
|
|
45
|
+
"Consolidate and optimize the route paths declared in _routes.json",
|
|
46
|
+
Functions.OptimizeRoutesOptions,
|
|
47
|
+
Functions.OptimizeRoutesHandler
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
.command("project", "⚡️ Interact with your Pages projects", (yargs) =>
|
|
62
51
|
yargs
|
|
63
52
|
.command(
|
|
64
53
|
"list",
|
|
65
|
-
"List
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
"List your Cloudflare Pages projects",
|
|
55
|
+
Projects.ListOptions,
|
|
56
|
+
Projects.ListHandler
|
|
68
57
|
)
|
|
69
58
|
.command(
|
|
70
|
-
"create [
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
"create [project-name]",
|
|
60
|
+
"Create a new Cloudflare Pages project",
|
|
61
|
+
Projects.CreateOptions,
|
|
62
|
+
Projects.CreateHandler
|
|
74
63
|
)
|
|
64
|
+
.command("upload [directory]", false, Upload.Options, Upload.Handler)
|
|
75
65
|
.epilogue(pagesBetaWarning)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
)
|
|
67
|
+
.command(
|
|
68
|
+
"deployment",
|
|
69
|
+
"🚀 Interact with the deployments of a project",
|
|
70
|
+
(yargs) =>
|
|
71
|
+
yargs
|
|
72
|
+
.command(
|
|
73
|
+
"list",
|
|
74
|
+
"List deployments in your Cloudflare Pages project",
|
|
75
|
+
Deployments.ListOptions,
|
|
76
|
+
Deployments.ListHandler
|
|
77
|
+
)
|
|
78
|
+
.command(
|
|
79
|
+
"create [directory]",
|
|
80
|
+
"🆙 Publish a directory of static assets as a Pages deployment",
|
|
81
|
+
Publish.Options,
|
|
82
|
+
Publish.Handler
|
|
83
|
+
)
|
|
84
|
+
.epilogue(pagesBetaWarning)
|
|
85
|
+
)
|
|
86
|
+
.command(
|
|
87
|
+
"publish [directory]",
|
|
88
|
+
"🆙 Publish a directory of static assets as a Pages deployment",
|
|
89
|
+
Publish.Options,
|
|
90
|
+
Publish.Handler
|
|
91
|
+
)
|
|
92
|
+
.epilogue(pagesBetaWarning)
|
|
93
|
+
);
|
|
84
94
|
};
|
package/src/pages/publish.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import * as metrics from "../metrics";
|
|
|
16
16
|
import { requireAuth } from "../user";
|
|
17
17
|
import { buildFunctions } from "./build";
|
|
18
18
|
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
|
|
19
|
+
import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
|
|
19
20
|
import {
|
|
20
21
|
isRoutesJSONSpec,
|
|
21
22
|
optimizeRoutesJSONSpec,
|
|
@@ -256,18 +257,24 @@ export const Handler = async ({
|
|
|
256
257
|
const routesOutputPath = join(tmpdir(), `_routes-${Math.random()}.json`);
|
|
257
258
|
if (existsSync(functionsDirectory)) {
|
|
258
259
|
const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
buildFunctions({
|
|
260
|
+
try {
|
|
261
|
+
await buildFunctions({
|
|
262
262
|
outfile,
|
|
263
263
|
functionsDirectory,
|
|
264
|
-
onEnd: () =>
|
|
264
|
+
onEnd: () => {},
|
|
265
265
|
buildOutputDirectory: dirname(outfile),
|
|
266
266
|
routesOutputPath,
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
});
|
|
268
|
+
builtFunctions = readFileSync(outfile, "utf-8");
|
|
269
|
+
} catch (e) {
|
|
270
|
+
if (e instanceof FunctionsNoRoutesError) {
|
|
271
|
+
logger.warn(
|
|
272
|
+
getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
|
|
273
|
+
);
|
|
274
|
+
} else {
|
|
275
|
+
throw e;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
271
278
|
}
|
|
272
279
|
|
|
273
280
|
const manifest = await upload({ directory, accountId, projectName });
|
|
@@ -305,32 +312,34 @@ export const Handler = async ({
|
|
|
305
312
|
_redirects = readFileSync(join(directory, "_redirects"), "utf-8");
|
|
306
313
|
} catch {}
|
|
307
314
|
|
|
308
|
-
try {
|
|
309
|
-
_routes = readFileSync(routesOutputPath, "utf-8");
|
|
310
|
-
} catch {}
|
|
311
|
-
|
|
312
315
|
try {
|
|
313
316
|
_workerJS = readFileSync(join(directory, "_worker.js"), "utf-8");
|
|
314
317
|
} catch {}
|
|
315
318
|
|
|
316
319
|
if (_headers) {
|
|
317
320
|
formData.append("_headers", new File([_headers], "_headers"));
|
|
321
|
+
logger.log(`✨ Uploading _headers`);
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
if (_redirects) {
|
|
321
325
|
formData.append("_redirects", new File([_redirects], "_redirects"));
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (_routes) {
|
|
325
|
-
formData.append("_routes.json", new File([_routes], "_routes.json"));
|
|
326
|
+
logger.log(`✨ Uploading _redirects`);
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
if (builtFunctions) {
|
|
329
330
|
formData.append("_worker.js", new File([builtFunctions], "_worker.js"));
|
|
331
|
+
logger.log(`✨ Uploading Functions`);
|
|
332
|
+
try {
|
|
333
|
+
_routes = readFileSync(routesOutputPath, "utf-8");
|
|
334
|
+
if (_routes) {
|
|
335
|
+
formData.append("_routes.json", new File([_routes], "_routes.json"));
|
|
336
|
+
}
|
|
337
|
+
} catch {}
|
|
330
338
|
} else if (_workerJS) {
|
|
331
339
|
// Advanced Mode
|
|
332
340
|
// https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
|
|
333
341
|
formData.append("_worker.js", new File([_workerJS], "_worker.js"));
|
|
342
|
+
logger.log(`✨ Uploading _worker.js`);
|
|
334
343
|
|
|
335
344
|
try {
|
|
336
345
|
// In advanced mode, developers can specify a custom _routes.json
|
|
@@ -348,6 +357,8 @@ export const Handler = async ({
|
|
|
348
357
|
}
|
|
349
358
|
|
|
350
359
|
_routes = JSON.stringify(optimizeRoutesJSONSpec(advancedModeRoutes));
|
|
360
|
+
formData.append("_routes.json", new File([_routes], "_routes.json"));
|
|
361
|
+
logger.log(`✨ Uploading _routes.json`);
|
|
351
362
|
|
|
352
363
|
logger.warn(
|
|
353
364
|
`🚨 _routes.json is an experimental feature and is subject to change. Don't use unless you really must!`
|
package/src/tail/filters.ts
CHANGED
|
@@ -58,13 +58,14 @@ type OutcomeFilter = {
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* There are five possible outcomes we can get, three of which
|
|
61
|
-
* (exception, exceededCpu, and unknown) are considered errors
|
|
61
|
+
* (exception, exceededCpu, exceededMemory, and unknown) are considered errors
|
|
62
62
|
*/
|
|
63
63
|
export type Outcome =
|
|
64
64
|
| "ok"
|
|
65
65
|
| "canceled"
|
|
66
66
|
| "exception"
|
|
67
67
|
| "exceededCpu"
|
|
68
|
+
| "exceededMemory"
|
|
68
69
|
| "unknown";
|
|
69
70
|
|
|
70
71
|
/**
|
|
@@ -210,6 +211,7 @@ function parseOutcome(
|
|
|
210
211
|
case "error":
|
|
211
212
|
outcomes.add("exception");
|
|
212
213
|
outcomes.add("exceededCpu");
|
|
214
|
+
outcomes.add("exceededMemory");
|
|
213
215
|
outcomes.add("unknown");
|
|
214
216
|
break;
|
|
215
217
|
|
package/src/tail/printing.ts
CHANGED
|
@@ -96,6 +96,8 @@ function prettifyOutcome(outcome: Outcome): string {
|
|
|
96
96
|
return "Canceled";
|
|
97
97
|
case "exceededCpu":
|
|
98
98
|
return "Exceeded CPU Limit";
|
|
99
|
+
case "exceededMemory":
|
|
100
|
+
return "Exceeded Memory Limit";
|
|
99
101
|
case "exception":
|
|
100
102
|
return "Exception Thrown";
|
|
101
103
|
case "unknown":
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { match } from "path-to-regexp";
|
|
2
2
|
|
|
3
|
+
//note: this explicitly does not include the * character, as pages requires this
|
|
4
|
+
const escapeRegex = /[.+?^${}()|[\]\\]/g;
|
|
5
|
+
|
|
3
6
|
type HTTPMethod =
|
|
4
7
|
| "HEAD"
|
|
5
8
|
| "OPTIONS"
|
|
@@ -67,8 +70,13 @@ function* executeRequest(request: Request, relativePathname: string) {
|
|
|
67
70
|
continue;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
const
|
|
73
|
+
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
|
|
74
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
75
|
+
end: false,
|
|
76
|
+
});
|
|
77
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
78
|
+
end: false,
|
|
79
|
+
});
|
|
72
80
|
const matchResult = routeMatcher(relativePathname);
|
|
73
81
|
const mountMatchResult = mountMatcher(relativePathname);
|
|
74
82
|
if (matchResult && mountMatchResult) {
|
|
@@ -88,8 +96,12 @@ function* executeRequest(request: Request, relativePathname: string) {
|
|
|
88
96
|
continue;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
const routeMatcher = match(route.routePath, {
|
|
92
|
-
|
|
99
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
100
|
+
end: true,
|
|
101
|
+
});
|
|
102
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
103
|
+
end: false,
|
|
104
|
+
});
|
|
93
105
|
const matchResult = routeMatcher(relativePathname);
|
|
94
106
|
const mountMatchResult = mountMatcher(relativePathname);
|
|
95
107
|
if (matchResult && mountMatchResult && route.modules.length) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { match } from "path-to-regexp";
|
|
2
2
|
|
|
3
|
+
//note: this explicitly does not include the * character, as pages requires this
|
|
4
|
+
const escapeRegex = /[.+?^${}()|[\]\\]/g;
|
|
5
|
+
|
|
3
6
|
type HTTPMethod =
|
|
4
7
|
| "HEAD"
|
|
5
8
|
| "OPTIONS"
|
|
@@ -61,8 +64,13 @@ function* executeRequest(request: Request) {
|
|
|
61
64
|
continue;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
const
|
|
67
|
+
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
|
|
68
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
69
|
+
end: false,
|
|
70
|
+
});
|
|
71
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
72
|
+
end: false,
|
|
73
|
+
});
|
|
66
74
|
const matchResult = routeMatcher(requestPath);
|
|
67
75
|
const mountMatchResult = mountMatcher(requestPath);
|
|
68
76
|
if (matchResult && mountMatchResult) {
|
|
@@ -81,9 +89,12 @@ function* executeRequest(request: Request) {
|
|
|
81
89
|
if (route.method && route.method !== request.method) {
|
|
82
90
|
continue;
|
|
83
91
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
93
|
+
end: true,
|
|
94
|
+
});
|
|
95
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
96
|
+
end: false,
|
|
97
|
+
});
|
|
87
98
|
const matchResult = routeMatcher(requestPath);
|
|
88
99
|
const mountMatchResult = mountMatcher(requestPath);
|
|
89
100
|
if (matchResult && mountMatchResult && route.modules.length) {
|
|
@@ -16,20 +16,23 @@ export default {
|
|
|
16
16
|
if (details) {
|
|
17
17
|
facadeEnv[name] = {
|
|
18
18
|
async fetch(...reqArgs) {
|
|
19
|
+
const reqFromArgs = new Request(...reqArgs);
|
|
19
20
|
if (details.headers) {
|
|
20
|
-
const req = new Request(...reqArgs);
|
|
21
21
|
for (const [key, value] of Object.entries(details.headers)) {
|
|
22
22
|
// In remote mode, you need to add a couple of headers
|
|
23
23
|
// to make sure it's talking to the 'dev' preview session
|
|
24
24
|
// (much like wrangler dev already does via proxy.ts)
|
|
25
|
-
|
|
25
|
+
reqFromArgs.headers.set(key, value);
|
|
26
26
|
}
|
|
27
|
-
return env[name].fetch(
|
|
27
|
+
return env[name].fetch(reqFromArgs);
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
|
|
30
|
+
const url = new URL(reqFromArgs.url);
|
|
31
|
+
url.protocol = details.protocol;
|
|
32
|
+
url.host = details.host;
|
|
33
|
+
if (details.port !== undefined) url.port = details.port;
|
|
34
|
+
|
|
35
|
+
const request = new Request(url.toString(), reqFromArgs);
|
|
33
36
|
return fetch(request);
|
|
34
37
|
},
|
|
35
38
|
};
|
|
@@ -7,20 +7,23 @@ for (const [name, details] of Object.entries(Workers)) {
|
|
|
7
7
|
if (details) {
|
|
8
8
|
globalThis[name] = {
|
|
9
9
|
async fetch(...reqArgs) {
|
|
10
|
+
const reqFromArgs = new Request(...reqArgs);
|
|
10
11
|
if (details.headers) {
|
|
11
|
-
const req = new Request(...reqArgs);
|
|
12
12
|
for (const [key, value] of Object.entries(details.headers)) {
|
|
13
13
|
// In remote mode, you need to add a couple of headers
|
|
14
14
|
// to make sure it's talking to the 'dev' preview session
|
|
15
15
|
// (much like wrangler dev already does via proxy.ts)
|
|
16
|
-
|
|
16
|
+
reqFromArgs.headers.set(key, value);
|
|
17
17
|
}
|
|
18
|
-
return env[name].fetch(
|
|
18
|
+
return env[name].fetch(reqFromArgs);
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
|
|
21
|
+
const url = new URL(reqFromArgs.url);
|
|
22
|
+
url.protocol = details.protocol;
|
|
23
|
+
url.host = details.host;
|
|
24
|
+
if (details.port !== undefined) url.port = details.port;
|
|
25
|
+
|
|
26
|
+
const request = new Request(url.toString(), reqFromArgs);
|
|
24
27
|
return fetch(request);
|
|
25
28
|
},
|
|
26
29
|
};
|
package/wrangler-dist/cli.d.ts
CHANGED