wrangler 2.0.27 → 2.1.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/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +1141 -369
- package/package.json +6 -4
- package/src/__tests__/api-dev.test.ts +19 -0
- package/src/__tests__/configuration.test.ts +27 -27
- package/src/__tests__/dev.test.tsx +8 -6
- package/src/__tests__/helpers/hello-world-worker.js +5 -0
- package/src/__tests__/helpers/mock-cfetch.ts +4 -4
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
- package/src/__tests__/helpers/mock-known-routes.ts +7 -0
- package/src/__tests__/index.test.ts +37 -37
- package/src/__tests__/init.test.ts +356 -5
- package/src/__tests__/jest.setup.ts +13 -0
- package/src/__tests__/middleware.test.ts +768 -0
- package/src/__tests__/pages.test.ts +829 -104
- package/src/__tests__/paths.test.ts +17 -0
- package/src/__tests__/publish.test.ts +512 -445
- package/src/__tests__/tail.test.ts +79 -72
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/worker-namespace.test.ts +37 -35
- package/src/api/dev.ts +93 -28
- package/src/bundle.ts +239 -12
- package/src/cfetch/internal.ts +64 -3
- package/src/cli.ts +1 -1
- package/src/config/environment.ts +1 -1
- package/src/config/index.ts +4 -4
- package/src/config/validation.ts +3 -3
- package/src/create-worker-upload-form.ts +29 -26
- package/src/dev/dev.tsx +3 -1
- package/src/dev/local.tsx +319 -171
- package/src/dev/remote.tsx +16 -4
- package/src/dev/start-server.ts +416 -0
- package/src/dev/use-esbuild.ts +4 -0
- package/src/dev.tsx +340 -166
- package/src/dialogs.tsx +12 -0
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
- package/src/entry.ts +2 -1
- package/src/index.tsx +59 -12
- package/src/init.ts +291 -16
- package/src/metrics/send-event.ts +6 -5
- package/src/miniflare-cli/assets.ts +130 -476
- package/src/miniflare-cli/index.ts +39 -33
- package/src/pages/constants.ts +3 -0
- package/src/pages/dev.tsx +8 -3
- package/src/pages/functions/buildPlugin.ts +2 -1
- package/src/pages/functions/buildWorker.ts +2 -1
- package/src/pages/functions/routes-transformation.test.ts +12 -1
- package/src/pages/functions/routes-transformation.ts +7 -1
- package/src/pages/hash.tsx +13 -0
- package/src/pages/publish.tsx +82 -38
- package/src/pages/upload.tsx +3 -18
- package/src/paths.ts +20 -1
- package/src/publish.ts +49 -8
- package/src/tail/filters.ts +1 -5
- package/src/tail/index.ts +6 -3
- package/src/worker.ts +10 -9
- package/src/zones.ts +91 -0
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +84 -0
- package/templates/middleware/loader-sw.ts +213 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +14 -0
- package/wrangler-dist/cli.d.ts +22 -8
- package/wrangler-dist/cli.js +71020 -65212
|
@@ -59,41 +59,45 @@ async function main() {
|
|
|
59
59
|
|
|
60
60
|
config.bindings = {
|
|
61
61
|
...config.bindings,
|
|
62
|
-
...
|
|
63
|
-
Object.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
const namespace = new DurableObjectNamespace(name as string, factory);
|
|
76
|
-
namespace.get = (id) => {
|
|
77
|
-
const stub = new DurableObjectStub(factory, id);
|
|
78
|
-
stub.fetch = (...reqArgs) => {
|
|
79
|
-
const requestFromArgs = new MiniflareRequest(...reqArgs);
|
|
80
|
-
const url = new URL(requestFromArgs.url);
|
|
81
|
-
url.host = host;
|
|
82
|
-
if (port !== undefined) url.port = port.toString();
|
|
83
|
-
const request = new MiniflareRequest(
|
|
84
|
-
url.toString(),
|
|
85
|
-
requestFromArgs
|
|
62
|
+
...(config.externalDurableObjects &&
|
|
63
|
+
Object.fromEntries(
|
|
64
|
+
Object.entries(
|
|
65
|
+
config.externalDurableObjects as Record<
|
|
66
|
+
string,
|
|
67
|
+
{ name: string; host: string; port: number }
|
|
68
|
+
>
|
|
69
|
+
).map(([binding, { name, host, port }]) => {
|
|
70
|
+
const factory = () => {
|
|
71
|
+
throw new FatalError(
|
|
72
|
+
"An external Durable Object instance's state has somehow been attempted to be accessed.",
|
|
73
|
+
1
|
|
86
74
|
);
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
};
|
|
76
|
+
const namespace = new DurableObjectNamespace(name as string, factory);
|
|
77
|
+
namespace.get = (id) => {
|
|
78
|
+
const stub = new DurableObjectStub(factory, id);
|
|
79
|
+
stub.fetch = (...reqArgs) => {
|
|
80
|
+
const requestFromArgs = new MiniflareRequest(...reqArgs);
|
|
81
|
+
const url = new URL(requestFromArgs.url);
|
|
82
|
+
url.host = host;
|
|
83
|
+
if (port !== undefined) url.port = port.toString();
|
|
84
|
+
const request = new MiniflareRequest(
|
|
85
|
+
url.toString(),
|
|
86
|
+
requestFromArgs
|
|
87
|
+
);
|
|
88
|
+
request.headers.set("x-miniflare-durable-object-name", name);
|
|
89
|
+
request.headers.set(
|
|
90
|
+
"x-miniflare-durable-object-id",
|
|
91
|
+
id.toString()
|
|
92
|
+
);
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
return fetch(request);
|
|
95
|
+
};
|
|
96
|
+
return stub;
|
|
91
97
|
};
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
})
|
|
96
|
-
),
|
|
98
|
+
return [binding, namespace];
|
|
99
|
+
})
|
|
100
|
+
)),
|
|
97
101
|
};
|
|
98
102
|
|
|
99
103
|
let mf: Miniflare | undefined;
|
|
@@ -125,7 +129,8 @@ async function main() {
|
|
|
125
129
|
}
|
|
126
130
|
mf = new Miniflare(config);
|
|
127
131
|
// Start Miniflare development server
|
|
128
|
-
await mf.startServer();
|
|
132
|
+
const mfServer = await mf.startServer();
|
|
133
|
+
const mfPort = (mfServer.address() as AddressInfo).port;
|
|
129
134
|
await mf.startScheduler();
|
|
130
135
|
|
|
131
136
|
const internalDurableObjectClassNames = Object.values(
|
|
@@ -191,6 +196,7 @@ async function main() {
|
|
|
191
196
|
process.send &&
|
|
192
197
|
process.send(
|
|
193
198
|
JSON.stringify({
|
|
199
|
+
mfPort: mfPort,
|
|
194
200
|
ready: true,
|
|
195
201
|
durableObjectsPort: durableObjectsMfPort,
|
|
196
202
|
})
|
package/src/pages/constants.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { version as wranglerVersion } from "../../package.json";
|
|
2
|
+
|
|
1
3
|
export const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
|
|
2
4
|
export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
|
|
3
5
|
export const MAX_BUCKET_FILE_COUNT = 5000;
|
|
@@ -10,3 +12,4 @@ export const isInPagesCI = !!process.env.CF_PAGES;
|
|
|
10
12
|
export const MAX_FUNCTIONS_ROUTES_RULES = 100;
|
|
11
13
|
export const MAX_FUNCTIONS_ROUTES_RULE_LENGTH = 100;
|
|
12
14
|
export const ROUTES_SPEC_VERSION = 1;
|
|
15
|
+
export const ROUTES_SPEC_DESCRIPTION = `Generated by wrangler@${wranglerVersion}`;
|
package/src/pages/dev.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { unstable_dev } from "../api";
|
|
|
8
8
|
import { FatalError } from "../errors";
|
|
9
9
|
import { logger } from "../logger";
|
|
10
10
|
import * as metrics from "../metrics";
|
|
11
|
+
import { getBasePath } from "../paths";
|
|
11
12
|
import { buildFunctions } from "./build";
|
|
12
13
|
import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
|
|
13
14
|
import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
|
|
@@ -274,7 +275,7 @@ export const Handler = async ({
|
|
|
274
275
|
|
|
275
276
|
if (!existsSync(scriptPath)) {
|
|
276
277
|
logger.log("No functions. Shimming...");
|
|
277
|
-
scriptPath = resolve(
|
|
278
|
+
scriptPath = resolve(getBasePath(), "templates/pages-shim.ts");
|
|
278
279
|
} else {
|
|
279
280
|
const runBuild = async () => {
|
|
280
281
|
try {
|
|
@@ -363,7 +364,7 @@ export const Handler = async ({
|
|
|
363
364
|
logLevel: "error",
|
|
364
365
|
logPrefix: "pages",
|
|
365
366
|
},
|
|
366
|
-
true
|
|
367
|
+
{ testMode: false, disableExperimentalWarning: true }
|
|
367
368
|
);
|
|
368
369
|
await metrics.sendMetricsEvent("run pages dev");
|
|
369
370
|
|
|
@@ -450,9 +451,13 @@ async function spawnProxyProcess({
|
|
|
450
451
|
command: (string | number)[];
|
|
451
452
|
}): Promise<undefined | number> {
|
|
452
453
|
if (command.length === 0) {
|
|
454
|
+
if (port !== undefined) {
|
|
455
|
+
return port;
|
|
456
|
+
}
|
|
457
|
+
|
|
453
458
|
CLEANUP();
|
|
454
459
|
throw new FatalError(
|
|
455
|
-
"Must specify a directory of static assets to serve or a command to run.",
|
|
460
|
+
"Must specify a directory of static assets to serve or a command to run or a proxy port.",
|
|
456
461
|
1
|
|
457
462
|
);
|
|
458
463
|
}
|
|
@@ -3,6 +3,7 @@ import { dirname, relative, resolve } from "node:path";
|
|
|
3
3
|
import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
|
|
4
4
|
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
|
|
5
5
|
import { build } from "esbuild";
|
|
6
|
+
import { getBasePath } from "../../paths";
|
|
6
7
|
|
|
7
8
|
type Options = {
|
|
8
9
|
routesModule: string;
|
|
@@ -24,7 +25,7 @@ export function buildPlugin({
|
|
|
24
25
|
onEnd = () => {},
|
|
25
26
|
}: Options) {
|
|
26
27
|
return build({
|
|
27
|
-
entryPoints: [resolve(
|
|
28
|
+
entryPoints: [resolve(getBasePath(), "templates/pages-template-plugin.ts")],
|
|
28
29
|
inject: [routesModule],
|
|
29
30
|
bundle: true,
|
|
30
31
|
format: "esm",
|
|
@@ -4,6 +4,7 @@ import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
|
|
|
4
4
|
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
|
|
5
5
|
import { build } from "esbuild";
|
|
6
6
|
import { nanoid } from "nanoid";
|
|
7
|
+
import { getBasePath } from "../../paths";
|
|
7
8
|
|
|
8
9
|
type Options = {
|
|
9
10
|
routesModule: string;
|
|
@@ -29,7 +30,7 @@ export function buildWorker({
|
|
|
29
30
|
nodeCompat,
|
|
30
31
|
}: Options) {
|
|
31
32
|
return build({
|
|
32
|
-
entryPoints: [resolve(
|
|
33
|
+
entryPoints: [resolve(getBasePath(), "templates/pages-template-worker.ts")],
|
|
33
34
|
inject: [routesModule],
|
|
34
35
|
bundle: true,
|
|
35
36
|
format: "esm",
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { toUrlPath } from "../../paths";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MAX_FUNCTIONS_ROUTES_RULES,
|
|
4
|
+
ROUTES_SPEC_VERSION,
|
|
5
|
+
ROUTES_SPEC_DESCRIPTION,
|
|
6
|
+
} from "../constants";
|
|
3
7
|
import {
|
|
4
8
|
compareRoutes,
|
|
5
9
|
convertRoutesToGlobPatterns,
|
|
@@ -164,6 +168,7 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
164
168
|
])
|
|
165
169
|
).toEqual({
|
|
166
170
|
version: ROUTES_SPEC_VERSION,
|
|
171
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
167
172
|
include: ["/middleware/*", "/foo/*", "/api/foo/bar"],
|
|
168
173
|
exclude: [],
|
|
169
174
|
});
|
|
@@ -176,6 +181,7 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
176
181
|
}
|
|
177
182
|
expect(convertRoutesToRoutesJSONSpec(routes)).toEqual({
|
|
178
183
|
version: ROUTES_SPEC_VERSION,
|
|
184
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
179
185
|
include: ["/*"],
|
|
180
186
|
exclude: [],
|
|
181
187
|
});
|
|
@@ -197,6 +203,7 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
197
203
|
expect(
|
|
198
204
|
optimizeRoutesJSONSpec({
|
|
199
205
|
version: ROUTES_SPEC_VERSION,
|
|
206
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
200
207
|
exclude: [],
|
|
201
208
|
include: [
|
|
202
209
|
"/api/foo/bar",
|
|
@@ -208,6 +215,7 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
208
215
|
})
|
|
209
216
|
).toEqual({
|
|
210
217
|
version: ROUTES_SPEC_VERSION,
|
|
218
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
211
219
|
include: ["/middleware/*", "/foo/*", "/api/foo/bar"],
|
|
212
220
|
exclude: [],
|
|
213
221
|
});
|
|
@@ -221,11 +229,13 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
221
229
|
expect(
|
|
222
230
|
optimizeRoutesJSONSpec({
|
|
223
231
|
version: ROUTES_SPEC_VERSION,
|
|
232
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
224
233
|
include,
|
|
225
234
|
exclude: [],
|
|
226
235
|
})
|
|
227
236
|
).toEqual({
|
|
228
237
|
version: ROUTES_SPEC_VERSION,
|
|
238
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
229
239
|
include: ["/*"],
|
|
230
240
|
exclude: [],
|
|
231
241
|
});
|
|
@@ -239,6 +249,7 @@ describe("route-paths-to-glob-patterns", () => {
|
|
|
239
249
|
expect(
|
|
240
250
|
optimizeRoutesJSONSpec({
|
|
241
251
|
version: ROUTES_SPEC_VERSION,
|
|
252
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
242
253
|
include,
|
|
243
254
|
exclude: [],
|
|
244
255
|
}).include.length
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { join as pathJoin } from "node:path";
|
|
2
2
|
import { toUrlPath } from "../../paths";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
MAX_FUNCTIONS_ROUTES_RULES,
|
|
5
|
+
ROUTES_SPEC_DESCRIPTION,
|
|
6
|
+
ROUTES_SPEC_VERSION,
|
|
7
|
+
} from "../constants";
|
|
4
8
|
import { consolidateRoutes } from "./routes-consolidation";
|
|
5
9
|
import type { RouteConfig } from "./routes";
|
|
6
10
|
|
|
7
11
|
/** Interface for _routes.json */
|
|
8
12
|
interface RoutesJSONSpec {
|
|
9
13
|
version: typeof ROUTES_SPEC_VERSION;
|
|
14
|
+
description?: string;
|
|
10
15
|
include: string[];
|
|
11
16
|
exclude: string[];
|
|
12
17
|
}
|
|
@@ -53,6 +58,7 @@ export function convertRoutesToRoutesJSONSpec(
|
|
|
53
58
|
const include = convertRoutesToGlobPatterns(reversedRoutes);
|
|
54
59
|
return optimizeRoutesJSONSpec({
|
|
55
60
|
version: ROUTES_SPEC_VERSION,
|
|
61
|
+
description: ROUTES_SPEC_DESCRIPTION,
|
|
56
62
|
include,
|
|
57
63
|
exclude: [],
|
|
58
64
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { hash as blake3hash } from "blake3-wasm";
|
|
4
|
+
|
|
5
|
+
export const hashFile = (filepath: string) => {
|
|
6
|
+
const contents = readFileSync(filepath);
|
|
7
|
+
const base64Contents = contents.toString("base64");
|
|
8
|
+
const extension = extname(filepath).substring(1);
|
|
9
|
+
|
|
10
|
+
return blake3hash(base64Contents + extension)
|
|
11
|
+
.toString("hex")
|
|
12
|
+
.slice(0, 32);
|
|
13
|
+
};
|
package/src/pages/publish.tsx
CHANGED
|
@@ -15,12 +15,9 @@ import { logger } from "../logger";
|
|
|
15
15
|
import * as metrics from "../metrics";
|
|
16
16
|
import { requireAuth } from "../user";
|
|
17
17
|
import { buildFunctions } from "./build";
|
|
18
|
-
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
|
|
18
|
+
import { PAGES_CONFIG_CACHE_FILENAME, ROUTES_SPEC_VERSION } from "./constants";
|
|
19
19
|
import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
|
|
20
|
-
import {
|
|
21
|
-
isRoutesJSONSpec,
|
|
22
|
-
optimizeRoutesJSONSpec,
|
|
23
|
-
} from "./functions/routes-transformation";
|
|
20
|
+
import { isRoutesJSONSpec } from "./functions/routes-transformation";
|
|
24
21
|
import { listProjects } from "./projects";
|
|
25
22
|
import { upload } from "./upload";
|
|
26
23
|
import { pagesBetaWarning } from "./utils";
|
|
@@ -254,7 +251,10 @@ export const Handler = async ({
|
|
|
254
251
|
|
|
255
252
|
let builtFunctions: string | undefined = undefined;
|
|
256
253
|
const functionsDirectory = join(cwd(), "functions");
|
|
257
|
-
const routesOutputPath = join(
|
|
254
|
+
const routesOutputPath = !existsSync(join(directory, "_routes.json"))
|
|
255
|
+
? join(tmpdir(), `_routes-${Math.random()}.json`)
|
|
256
|
+
: undefined;
|
|
257
|
+
|
|
258
258
|
if (existsSync(functionsDirectory)) {
|
|
259
259
|
const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
|
|
260
260
|
try {
|
|
@@ -265,6 +265,7 @@ export const Handler = async ({
|
|
|
265
265
|
buildOutputDirectory: dirname(outfile),
|
|
266
266
|
routesOutputPath,
|
|
267
267
|
});
|
|
268
|
+
|
|
268
269
|
builtFunctions = readFileSync(outfile, "utf-8");
|
|
269
270
|
} catch (e) {
|
|
270
271
|
if (e instanceof FunctionsNoRoutesError) {
|
|
@@ -301,7 +302,8 @@ export const Handler = async ({
|
|
|
301
302
|
|
|
302
303
|
let _headers: string | undefined,
|
|
303
304
|
_redirects: string | undefined,
|
|
304
|
-
|
|
305
|
+
_routesGenerated: string | undefined,
|
|
306
|
+
_routesCustom: string | undefined,
|
|
305
307
|
_workerJS: string | undefined;
|
|
306
308
|
|
|
307
309
|
try {
|
|
@@ -312,6 +314,12 @@ export const Handler = async ({
|
|
|
312
314
|
_redirects = readFileSync(join(directory, "_redirects"), "utf-8");
|
|
313
315
|
} catch {}
|
|
314
316
|
|
|
317
|
+
try {
|
|
318
|
+
// Developers can specify a custom _routes.json file, for projects with Pages
|
|
319
|
+
// Functions or projects in Advanced Mode
|
|
320
|
+
_routesCustom = readFileSync(join(directory, "_routes.json"), "utf-8");
|
|
321
|
+
} catch {}
|
|
322
|
+
|
|
315
323
|
try {
|
|
316
324
|
_workerJS = readFileSync(join(directory, "_worker.js"), "utf-8");
|
|
317
325
|
} catch {}
|
|
@@ -327,47 +335,65 @@ export const Handler = async ({
|
|
|
327
335
|
}
|
|
328
336
|
|
|
329
337
|
if (builtFunctions) {
|
|
338
|
+
// with Pages Functions
|
|
339
|
+
// https://developers.cloudflare.com/pages/platform/functions/
|
|
330
340
|
formData.append("_worker.js", new File([builtFunctions], "_worker.js"));
|
|
331
341
|
logger.log(`✨ Uploading Functions`);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
342
|
+
|
|
343
|
+
if (_routesCustom) {
|
|
344
|
+
// user provided a custom _routes.json file
|
|
345
|
+
try {
|
|
346
|
+
validateRoutesFile(_routesCustom, join(directory, "_routes.json"));
|
|
347
|
+
|
|
348
|
+
formData.append(
|
|
349
|
+
"_routes.json",
|
|
350
|
+
new File([_routesCustom], "_routes.json")
|
|
351
|
+
);
|
|
352
|
+
logger.log(`✨ Uploading _routes.json`);
|
|
353
|
+
logger.warn(
|
|
354
|
+
`_routes.json is an experimental feature and is subject to change. Please use with care.`
|
|
355
|
+
);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
if (err instanceof FatalError) {
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
336
360
|
}
|
|
337
|
-
}
|
|
361
|
+
} else if (routesOutputPath) {
|
|
362
|
+
// no custom _routes.json file found, so fallback to the generated one
|
|
363
|
+
try {
|
|
364
|
+
_routesGenerated = readFileSync(routesOutputPath, "utf-8");
|
|
365
|
+
|
|
366
|
+
if (_routesGenerated) {
|
|
367
|
+
formData.append(
|
|
368
|
+
"_routes.json",
|
|
369
|
+
new File([_routesGenerated], "_routes.json")
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
} catch {}
|
|
373
|
+
}
|
|
338
374
|
} else if (_workerJS) {
|
|
339
375
|
// Advanced Mode
|
|
340
376
|
// https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
|
|
341
377
|
formData.append("_worker.js", new File([_workerJS], "_worker.js"));
|
|
342
378
|
logger.log(`✨ Uploading _worker.js`);
|
|
343
379
|
|
|
344
|
-
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const routesPath = join(directory, "_routes.json");
|
|
349
|
-
const advancedModeRoutesString = readFileSync(routesPath, "utf-8");
|
|
350
|
-
const advancedModeRoutes = JSON.parse(advancedModeRoutesString);
|
|
351
|
-
|
|
352
|
-
if (!isRoutesJSONSpec(advancedModeRoutes)) {
|
|
353
|
-
throw new FatalError(
|
|
354
|
-
"Invalid _routes.json file found at:" + routesPath,
|
|
355
|
-
1
|
|
356
|
-
);
|
|
357
|
-
}
|
|
380
|
+
if (_routesCustom) {
|
|
381
|
+
// user provided a custom _routes.json file
|
|
382
|
+
try {
|
|
383
|
+
validateRoutesFile(_routesCustom, join(directory, "_routes.json"));
|
|
358
384
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
385
|
+
formData.append(
|
|
386
|
+
"_routes.json",
|
|
387
|
+
new File([_routesCustom], "_routes.json")
|
|
388
|
+
);
|
|
389
|
+
logger.log(`✨ Uploading _routes.json`);
|
|
390
|
+
logger.warn(
|
|
391
|
+
`_routes.json is an experimental feature and is subject to change. Please use with care.`
|
|
392
|
+
);
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (err instanceof FatalError) {
|
|
395
|
+
throw err;
|
|
396
|
+
}
|
|
371
397
|
}
|
|
372
398
|
}
|
|
373
399
|
}
|
|
@@ -389,3 +415,21 @@ export const Handler = async ({
|
|
|
389
415
|
);
|
|
390
416
|
await metrics.sendMetricsEvent("create pages deployment");
|
|
391
417
|
};
|
|
418
|
+
|
|
419
|
+
function validateRoutesFile(_routes: string, routesPath: string) {
|
|
420
|
+
const routes = JSON.parse(_routes);
|
|
421
|
+
|
|
422
|
+
if (!isRoutesJSONSpec(routes)) {
|
|
423
|
+
throw new FatalError(
|
|
424
|
+
`Invalid _routes.json file found at: ${routesPath}. Please make sure the JSON object has the following format:
|
|
425
|
+
{
|
|
426
|
+
version: ${ROUTES_SPEC_VERSION};
|
|
427
|
+
include: string[];
|
|
428
|
+
exclude: string[];
|
|
429
|
+
}
|
|
430
|
+
and that at least one include rule is provided.
|
|
431
|
+
`,
|
|
432
|
+
1
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
package/src/pages/upload.tsx
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
basename,
|
|
4
|
-
dirname,
|
|
5
|
-
extname,
|
|
6
|
-
join,
|
|
7
|
-
relative,
|
|
8
|
-
resolve,
|
|
9
|
-
sep,
|
|
10
|
-
} from "node:path";
|
|
11
|
-
import { hash as blake3hash } from "blake3-wasm";
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
12
3
|
import { render, Text } from "ink";
|
|
13
4
|
import Spinner from "ink-spinner";
|
|
14
5
|
import { getType } from "mime";
|
|
@@ -25,6 +16,7 @@ import {
|
|
|
25
16
|
MAX_CHECK_MISSING_ATTEMPTS,
|
|
26
17
|
MAX_UPLOAD_ATTEMPTS,
|
|
27
18
|
} from "./constants";
|
|
19
|
+
import { hashFile } from "./hash";
|
|
28
20
|
import { pagesBetaWarning } from "./utils";
|
|
29
21
|
import type { UploadPayloadFile, YargsOptionsToInterface } from "./types";
|
|
30
22
|
import type { Argv } from "yargs";
|
|
@@ -144,11 +136,6 @@ export const upload = async (
|
|
|
144
136
|
} else {
|
|
145
137
|
const name = relative(startingDir, filepath).split(sep).join("/");
|
|
146
138
|
|
|
147
|
-
const fileContent = await readFile(filepath);
|
|
148
|
-
|
|
149
|
-
const base64Content = fileContent.toString("base64");
|
|
150
|
-
const extension = extname(basename(name)).substring(1);
|
|
151
|
-
|
|
152
139
|
if (filestat.size > 25 * 1024 * 1024) {
|
|
153
140
|
throw new FatalError(
|
|
154
141
|
`Error: Pages only supports files up to ${prettyBytes(
|
|
@@ -163,9 +150,7 @@ export const upload = async (
|
|
|
163
150
|
path: filepath,
|
|
164
151
|
contentType: getType(name) || "application/octet-stream",
|
|
165
152
|
sizeInBytes: filestat.size,
|
|
166
|
-
hash:
|
|
167
|
-
.toString("hex")
|
|
168
|
-
.slice(0, 32),
|
|
153
|
+
hash: hashFile(filepath),
|
|
169
154
|
});
|
|
170
155
|
}
|
|
171
156
|
})
|
package/src/paths.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { assert } from "console";
|
|
1
|
+
import { assert } from "node:console";
|
|
2
|
+
import { resolve } from "node:path";
|
|
2
3
|
|
|
3
4
|
type DiscriminatedPath<Discriminator extends string> = string & {
|
|
4
5
|
_discriminator: Discriminator;
|
|
@@ -24,3 +25,21 @@ export function toUrlPath(path: string): UrlPath {
|
|
|
24
25
|
);
|
|
25
26
|
return path.replace(/\\/g, "/") as UrlPath;
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The __RELATIVE_PACKAGE_PATH__ is defined either in the esbuild config (for production)
|
|
31
|
+
* or the jest.setup.ts (for unit testing).
|
|
32
|
+
*/
|
|
33
|
+
declare const __RELATIVE_PACKAGE_PATH__: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Use this function (rather than node.js constants like `__dirname`) to specify
|
|
37
|
+
* paths that are relative to the base path of the Wrangler package.
|
|
38
|
+
*
|
|
39
|
+
* It is important to use this function because it reliably maps to the root of the package
|
|
40
|
+
* no matter whether the code has been bundled or not.
|
|
41
|
+
*/
|
|
42
|
+
export function getBasePath(): string {
|
|
43
|
+
// eslint-disable-next-line no-restricted-globals
|
|
44
|
+
return resolve(__dirname, __RELATIVE_PACKAGE_PATH__);
|
|
45
|
+
}
|
package/src/publish.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { printBundleSize } from "./bundle-reporter";
|
|
|
8
8
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
9
9
|
import { printBindings } from "./config";
|
|
10
10
|
import { createWorkerUploadForm } from "./create-worker-upload-form";
|
|
11
|
-
import { confirm } from "./dialogs";
|
|
11
|
+
import { confirm, fromDashMessagePrompt } from "./dialogs";
|
|
12
12
|
import { getMigrationsToUpload } from "./durable";
|
|
13
13
|
import { logger } from "./logger";
|
|
14
14
|
import { getMetricsUsageHeaders } from "./metrics";
|
|
@@ -36,6 +36,8 @@ type Props = {
|
|
|
36
36
|
compatibilityDate: string | undefined;
|
|
37
37
|
compatibilityFlags: string[] | undefined;
|
|
38
38
|
assetPaths: AssetPaths | undefined;
|
|
39
|
+
vars: Record<string, string> | undefined;
|
|
40
|
+
defines: Record<string, string> | undefined;
|
|
39
41
|
triggers: string[] | undefined;
|
|
40
42
|
routes: string[] | undefined;
|
|
41
43
|
legacyEnv: boolean | undefined;
|
|
@@ -216,11 +218,30 @@ Update them to point to this script instead?`;
|
|
|
216
218
|
|
|
217
219
|
export default async function publish(props: Props): Promise<void> {
|
|
218
220
|
// TODO: warn if git/hg has uncommitted changes
|
|
219
|
-
const { config, accountId } = props;
|
|
221
|
+
const { config, accountId, name } = props;
|
|
222
|
+
if (accountId && name) {
|
|
223
|
+
const serviceMetaData = await fetchResult(
|
|
224
|
+
`/accounts/${accountId}/workers/services/${name}`
|
|
225
|
+
);
|
|
226
|
+
const { default_environment } = serviceMetaData as {
|
|
227
|
+
default_environment: {
|
|
228
|
+
script: { last_deployed_from: "dash" | "wrangler" | "api" };
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
(await fromDashMessagePrompt(
|
|
234
|
+
default_environment.script.last_deployed_from
|
|
235
|
+
)) === false
|
|
236
|
+
)
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
220
239
|
|
|
221
240
|
if (!(props.compatibilityDate || config.compatibility_date)) {
|
|
222
241
|
const compatibilityDateStr = `${new Date().getFullYear()}-${(
|
|
223
|
-
new Date().getMonth() +
|
|
242
|
+
new Date().getMonth() +
|
|
243
|
+
1 +
|
|
244
|
+
""
|
|
224
245
|
).padStart(2, "0")}-${(new Date().getDate() + "").padStart(2, "0")}`;
|
|
225
246
|
|
|
226
247
|
throw new Error(`A compatibility_date is required when publishing. Add the following to your wrangler.toml file:.
|
|
@@ -382,7 +403,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
382
403
|
tsconfig: props.tsconfig ?? config.tsconfig,
|
|
383
404
|
minify,
|
|
384
405
|
nodeCompat,
|
|
385
|
-
define: config.define,
|
|
406
|
+
define: { ...config.define, ...props.defines },
|
|
386
407
|
checkFetch: false,
|
|
387
408
|
assets: config.assets && {
|
|
388
409
|
...config.assets,
|
|
@@ -395,6 +416,9 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
395
416
|
// facades on top of it
|
|
396
417
|
workerDefinitions: undefined,
|
|
397
418
|
firstPartyWorkerDevFacade: false,
|
|
419
|
+
// We want to know if the build is for development or publishing
|
|
420
|
+
// This could potentially cause issues as we no longer have identical behaviour between dev and publish?
|
|
421
|
+
targetConsumer: "publish",
|
|
398
422
|
}
|
|
399
423
|
);
|
|
400
424
|
|
|
@@ -430,7 +454,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
430
454
|
? { binding: "__STATIC_CONTENT", id: assets.namespace }
|
|
431
455
|
: []
|
|
432
456
|
),
|
|
433
|
-
vars: config.vars,
|
|
457
|
+
vars: { ...config.vars, ...props.vars },
|
|
434
458
|
wasm_modules: config.wasm_modules,
|
|
435
459
|
text_blobs: {
|
|
436
460
|
...config.text_blobs,
|
|
@@ -443,7 +467,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
443
467
|
durable_objects: config.durable_objects,
|
|
444
468
|
r2_buckets: config.r2_buckets,
|
|
445
469
|
services: config.services,
|
|
446
|
-
|
|
470
|
+
dispatch_namespaces: config.dispatch_namespaces,
|
|
447
471
|
logfwdr: config.logfwdr,
|
|
448
472
|
unsafe: config.unsafe?.bindings,
|
|
449
473
|
};
|
|
@@ -470,19 +494,36 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
470
494
|
compatibility_flags:
|
|
471
495
|
props.compatibilityFlags ?? config.compatibility_flags,
|
|
472
496
|
usage_model: config.usage_model,
|
|
497
|
+
keep_bindings: true,
|
|
473
498
|
};
|
|
474
499
|
|
|
475
|
-
|
|
500
|
+
// As this is not deterministic for testing, we detect if in a jest environment and run asynchronously
|
|
501
|
+
// We do not care about the timing outside of testing
|
|
502
|
+
const bundleSizePromise = printBundleSize(
|
|
476
503
|
{ name: path.basename(resolvedEntryPointPath), content: content },
|
|
477
504
|
modules
|
|
478
505
|
);
|
|
506
|
+
if (process.env.JEST_WORKER_ID !== undefined) await bundleSizePromise;
|
|
507
|
+
else void bundleSizePromise;
|
|
479
508
|
|
|
480
509
|
const withoutStaticAssets = {
|
|
481
510
|
...bindings,
|
|
482
511
|
kv_namespaces: config.kv_namespaces,
|
|
483
512
|
text_blobs: config.text_blobs,
|
|
484
513
|
};
|
|
485
|
-
|
|
514
|
+
|
|
515
|
+
// mask anything that was overridden in cli args
|
|
516
|
+
// so that we don't log potential secrets into the terminal
|
|
517
|
+
const maskedVars = { ...withoutStaticAssets.vars };
|
|
518
|
+
for (const key of Object.keys(maskedVars)) {
|
|
519
|
+
if (maskedVars[key] !== config.vars[key]) {
|
|
520
|
+
// This means it was overridden in cli args
|
|
521
|
+
// so let's mask it
|
|
522
|
+
maskedVars[key] = "(hidden)";
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
printBindings({ ...withoutStaticAssets, vars: maskedVars });
|
|
486
527
|
|
|
487
528
|
if (!props.dryRun) {
|
|
488
529
|
// Upload the script so it has time to propagate.
|