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.
Files changed (65) hide show
  1. package/bin/wrangler.js +1 -1
  2. package/miniflare-dist/index.mjs +1141 -369
  3. package/package.json +6 -4
  4. package/src/__tests__/api-dev.test.ts +19 -0
  5. package/src/__tests__/configuration.test.ts +27 -27
  6. package/src/__tests__/dev.test.tsx +8 -6
  7. package/src/__tests__/helpers/hello-world-worker.js +5 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +4 -4
  9. package/src/__tests__/helpers/mock-console.ts +11 -2
  10. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  11. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  12. package/src/__tests__/index.test.ts +37 -37
  13. package/src/__tests__/init.test.ts +356 -5
  14. package/src/__tests__/jest.setup.ts +13 -0
  15. package/src/__tests__/middleware.test.ts +768 -0
  16. package/src/__tests__/pages.test.ts +829 -104
  17. package/src/__tests__/paths.test.ts +17 -0
  18. package/src/__tests__/publish.test.ts +512 -445
  19. package/src/__tests__/tail.test.ts +79 -72
  20. package/src/__tests__/test-old-node-version.js +3 -3
  21. package/src/__tests__/worker-namespace.test.ts +37 -35
  22. package/src/api/dev.ts +93 -28
  23. package/src/bundle.ts +239 -12
  24. package/src/cfetch/internal.ts +64 -3
  25. package/src/cli.ts +1 -1
  26. package/src/config/environment.ts +1 -1
  27. package/src/config/index.ts +4 -4
  28. package/src/config/validation.ts +3 -3
  29. package/src/create-worker-upload-form.ts +29 -26
  30. package/src/dev/dev.tsx +3 -1
  31. package/src/dev/local.tsx +319 -171
  32. package/src/dev/remote.tsx +16 -4
  33. package/src/dev/start-server.ts +416 -0
  34. package/src/dev/use-esbuild.ts +4 -0
  35. package/src/dev.tsx +340 -166
  36. package/src/dialogs.tsx +12 -0
  37. package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
  38. package/src/entry.ts +2 -1
  39. package/src/index.tsx +59 -12
  40. package/src/init.ts +291 -16
  41. package/src/metrics/send-event.ts +6 -5
  42. package/src/miniflare-cli/assets.ts +130 -476
  43. package/src/miniflare-cli/index.ts +39 -33
  44. package/src/pages/constants.ts +3 -0
  45. package/src/pages/dev.tsx +8 -3
  46. package/src/pages/functions/buildPlugin.ts +2 -1
  47. package/src/pages/functions/buildWorker.ts +2 -1
  48. package/src/pages/functions/routes-transformation.test.ts +12 -1
  49. package/src/pages/functions/routes-transformation.ts +7 -1
  50. package/src/pages/hash.tsx +13 -0
  51. package/src/pages/publish.tsx +82 -38
  52. package/src/pages/upload.tsx +3 -18
  53. package/src/paths.ts +20 -1
  54. package/src/publish.ts +49 -8
  55. package/src/tail/filters.ts +1 -5
  56. package/src/tail/index.ts +6 -3
  57. package/src/worker.ts +10 -9
  58. package/src/zones.ts +91 -0
  59. package/templates/middleware/common.ts +62 -0
  60. package/templates/middleware/loader-modules.ts +84 -0
  61. package/templates/middleware/loader-sw.ts +213 -0
  62. package/templates/middleware/middleware-pretty-error.ts +40 -0
  63. package/templates/middleware/middleware-scheduled.ts +14 -0
  64. package/wrangler-dist/cli.d.ts +22 -8
  65. 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
- ...Object.fromEntries(
63
- Object.entries(
64
- config.externalDurableObjects as Record<
65
- string,
66
- { name: string; host: string; port: number }
67
- >
68
- ).map(([binding, { name, host, port }]) => {
69
- const factory = () => {
70
- throw new FatalError(
71
- "An external Durable Object instance's state has somehow been attempted to be accessed.",
72
- 1
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
- request.headers.set("x-miniflare-durable-object-name", name);
88
- request.headers.set("x-miniflare-durable-object-id", id.toString());
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
- return fetch(request);
94
+ return fetch(request);
95
+ };
96
+ return stub;
91
97
  };
92
- return stub;
93
- };
94
- return [binding, namespace];
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
  })
@@ -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(__dirname, "../templates/pages-shim.ts");
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(__dirname, "../templates/pages-template-plugin.ts")],
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(__dirname, "../templates/pages-template-worker.ts")],
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 { MAX_FUNCTIONS_ROUTES_RULES, ROUTES_SPEC_VERSION } from "../constants";
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 { MAX_FUNCTIONS_ROUTES_RULES, ROUTES_SPEC_VERSION } from "../constants";
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
+ };
@@ -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(tmpdir(), `_routes-${Math.random()}.json`);
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
- _routes: string | undefined,
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
- try {
333
- _routes = readFileSync(routesOutputPath, "utf-8");
334
- if (_routes) {
335
- formData.append("_routes.json", new File([_routes], "_routes.json"));
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
- } catch {}
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
- try {
345
- // In advanced mode, developers can specify a custom _routes.json
346
- // file. In which case, we need to run it through optimization
347
- // to potentially reduce the overall worker pipeline size
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
- _routes = JSON.stringify(optimizeRoutesJSONSpec(advancedModeRoutes));
360
- formData.append("_routes.json", new File([_routes], "_routes.json"));
361
- logger.log(`✨ Uploading _routes.json`);
362
-
363
- logger.warn(
364
- `🚨 _routes.json is an experimental feature and is subject to change. Don't use unless you really must!`
365
- );
366
- } catch (e) {
367
- // Ignore file not existing errors for _routes.json but forward the potential
368
- // FatalError from an invalid spec
369
- if (e instanceof FatalError) {
370
- throw e;
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
+ }
@@ -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: blake3hash(base64Content + extension)
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
- worker_namespaces: config.worker_namespaces,
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
- void printBundleSize(
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
- printBindings(withoutStaticAssets);
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.