wrangler 0.0.0-e043479 → 0.0.0-e0a0509

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 (96) hide show
  1. package/README.md +27 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4886 -0
  4. package/package.json +38 -25
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +136 -25
  8. package/pages/functions/filepath-routing.ts +14 -3
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +1294 -355
  14. package/src/__tests__/dev.test.tsx +592 -12
  15. package/src/__tests__/guess-worker-format.test.ts +37 -18
  16. package/src/__tests__/helpers/mock-account-id.ts +4 -0
  17. package/src/__tests__/helpers/mock-cfetch.ts +8 -4
  18. package/src/__tests__/helpers/mock-console.ts +20 -3
  19. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  20. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  21. package/src/__tests__/helpers/mock-istty.ts +27 -0
  22. package/src/__tests__/helpers/mock-kv.ts +9 -16
  23. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  24. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  25. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  26. package/src/__tests__/https-options.test.ts +130 -0
  27. package/src/__tests__/index.test.ts +129 -753
  28. package/src/__tests__/init.test.ts +1516 -0
  29. package/src/__tests__/jest.setup.ts +13 -5
  30. package/src/__tests__/kv.test.ts +208 -174
  31. package/src/__tests__/logger.test.ts +115 -0
  32. package/src/__tests__/pages.test.ts +327 -0
  33. package/src/__tests__/parse.test.ts +4 -0
  34. package/src/__tests__/publish.test.ts +3313 -505
  35. package/src/__tests__/r2.test.ts +38 -26
  36. package/src/__tests__/route.test.ts +45 -0
  37. package/src/__tests__/secret.test.ts +55 -34
  38. package/src/__tests__/tail.test.ts +146 -20
  39. package/src/__tests__/user.test.ts +131 -0
  40. package/src/__tests__/whoami.test.tsx +26 -7
  41. package/src/bundle.ts +43 -7
  42. package/src/cfetch/internal.ts +9 -5
  43. package/src/cli.ts +5 -11
  44. package/src/config/README.md +7 -7
  45. package/src/config/config.ts +69 -105
  46. package/src/config/environment.ts +93 -4
  47. package/src/config/index.ts +15 -4
  48. package/src/config/validation-helpers.ts +228 -23
  49. package/src/config/validation.ts +559 -154
  50. package/src/config-cache.ts +45 -0
  51. package/src/{api/preview.ts → create-worker-preview.ts} +41 -13
  52. package/src/{api/form_data.ts → create-worker-upload-form.ts} +33 -10
  53. package/src/dev/dev-vars.ts +36 -0
  54. package/src/dev/dev.tsx +418 -0
  55. package/src/dev/local.tsx +312 -0
  56. package/src/dev/remote.tsx +213 -0
  57. package/src/dev/use-esbuild.ts +118 -0
  58. package/src/dialogs.tsx +16 -6
  59. package/src/entry.ts +222 -12
  60. package/src/environment-variables.ts +3 -1
  61. package/src/errors.ts +11 -0
  62. package/src/https-options.ts +123 -0
  63. package/src/index.tsx +878 -482
  64. package/src/inspect.ts +25 -11
  65. package/src/intl-polyfill.d.ts +139 -0
  66. package/src/{kv.tsx → kv.ts} +7 -27
  67. package/src/logger.ts +66 -0
  68. package/src/miniflare-cli/README.md +30 -0
  69. package/src/miniflare-cli/enum-keys.ts +17 -0
  70. package/src/miniflare-cli/index.ts +41 -0
  71. package/src/miniflare-cli/request-context.ts +40 -0
  72. package/src/module-collection.ts +94 -6
  73. package/src/open-in-browser.ts +18 -5
  74. package/src/package-manager.ts +9 -8
  75. package/src/pages.tsx +892 -62
  76. package/src/parse.ts +4 -2
  77. package/src/proxy.ts +128 -55
  78. package/src/publish.ts +305 -199
  79. package/src/selfsigned.d.ts +29 -0
  80. package/src/sites.tsx +88 -33
  81. package/src/tail/index.ts +2 -2
  82. package/src/tail/printing.ts +13 -8
  83. package/src/update-check.ts +19 -0
  84. package/src/user.tsx +244 -104
  85. package/src/whoami.tsx +2 -1
  86. package/src/{api/worker.ts → worker.ts} +15 -29
  87. package/templates/gitignore +171 -0
  88. package/templates/static-asset-facade.js +1 -1
  89. package/wrangler-dist/cli.js +23568 -50489
  90. package/src/__tests__/helpers/mock-user.ts +0 -27
  91. package/src/__tests__/logout.test.ts +0 -50
  92. package/src/__tests__/sentry.test.ts +0 -146
  93. package/src/dev.tsx +0 -960
  94. package/src/guess-worker-format.ts +0 -68
  95. package/src/reporting.ts +0 -115
  96. package/wrangler-dist/cli.js.map +0 -7
package/src/index.tsx CHANGED
@@ -3,19 +3,24 @@ import { writeFile, mkdir } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { setTimeout } from "node:timers/promises";
5
5
  import TOML from "@iarna/toml";
6
+ import chalk from "chalk";
7
+ import { execa } from "execa";
6
8
  import { findUp } from "find-up";
7
9
  import getPort from "get-port";
8
10
  import { render } from "ink";
9
11
  import React from "react";
10
12
  import onExit from "signal-exit";
13
+ import supportsColor from "supports-color";
11
14
  import makeCLI from "yargs";
12
15
  import { version as wranglerVersion } from "../package.json";
13
- import { toFormData } from "./api/form_data";
14
16
  import { fetchResult } from "./cfetch";
15
17
  import { findWranglerToml, readConfig } from "./config";
16
- import Dev from "./dev";
18
+ import { createWorkerUploadForm } from "./create-worker-upload-form";
19
+ import Dev from "./dev/dev";
20
+ import { getVarsForDev } from "./dev/dev-vars";
17
21
  import { confirm, prompt } from "./dialogs";
18
22
  import { getEntry } from "./entry";
23
+ import { DeprecationError } from "./errors";
19
24
  import {
20
25
  getNamespaceId,
21
26
  listNamespaces,
@@ -29,9 +34,16 @@ import {
29
34
  isKeyValue,
30
35
  unexpectedKeyValueProps,
31
36
  } from "./kv";
37
+ import { logger } from "./logger";
32
38
  import { getPackageManager } from "./package-manager";
33
- import { pages } from "./pages";
34
- import { formatMessage, ParseError, parseJSON, readFileSync } from "./parse";
39
+ import { pages, pagesBetaWarning } from "./pages";
40
+ import {
41
+ formatMessage,
42
+ ParseError,
43
+ parseJSON,
44
+ parseTOML,
45
+ readFileSync,
46
+ } from "./parse";
35
47
  import publish from "./publish";
36
48
  import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
37
49
  import { getAssetPaths } from "./sites";
@@ -41,26 +53,27 @@ import {
41
53
  prettyPrintLogs,
42
54
  translateCLICommandToFilterMessage,
43
55
  } from "./tail";
56
+ import { updateCheck } from "./update-check";
44
57
  import {
45
58
  login,
46
59
  logout,
47
60
  listScopes,
48
- initialise as initialiseUserConfig,
49
- loginOrRefreshIfRequired,
50
- getAccountId,
51
61
  validateScopeKeys,
62
+ requireAuth,
52
63
  } from "./user";
53
64
  import { whoami } from "./whoami";
54
65
 
55
66
  import type { Config } from "./config";
56
67
  import type { TailCLIFilters } from "./tail";
57
68
  import type { RawData } from "ws";
69
+ import type { CommandModule } from "yargs";
58
70
  import type Yargs from "yargs";
59
71
 
60
72
  type ConfigPath = string | undefined;
61
73
 
62
74
  const resetColor = "\x1b[0m";
63
75
  const fgGreenColor = "\x1b[32m";
76
+ const DEFAULT_LOCAL_PORT = 8787;
64
77
 
65
78
  function getRules(config: Config): Config["rules"] {
66
79
  const rules = config.rules ?? config.build?.upload?.rules ?? [];
@@ -72,8 +85,8 @@ function getRules(config: Config): Config["rules"] {
72
85
  }
73
86
 
74
87
  if (config.build?.upload?.rules) {
75
- console.warn(
76
- `Deprecation notice: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
88
+ logger.warn(
89
+ `Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
77
90
 
78
91
  ${TOML.stringify({ rules: config.build.upload.rules })}`
79
92
  );
@@ -81,42 +94,56 @@ ${TOML.stringify({ rules: config.build.upload.rules })}`
81
94
  return rules;
82
95
  }
83
96
 
84
- function isLegacyEnv(args: unknown, config: Config): boolean {
85
- return (
86
- (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
87
- config.legacy_env
97
+ async function printWranglerBanner() {
98
+ // Let's not print this in tests
99
+ if (typeof jest !== "undefined") {
100
+ return;
101
+ }
102
+
103
+ const text = ` ⛅️ wrangler ${wranglerVersion} ${await updateCheck()}`;
104
+
105
+ logger.log(
106
+ text +
107
+ "\n" +
108
+ (supportsColor.stdout
109
+ ? chalk.hex("#FF8800")("-".repeat(text.length))
110
+ : "-".repeat(text.length))
88
111
  );
89
112
  }
90
113
 
114
+ function isLegacyEnv(config: Config): boolean {
115
+ // We only read from config here, because we've already accounted for
116
+ // args["legacy-env"] in https://github.com/cloudflare/wrangler2/blob/b24aeb5722370c2e04bce97a84a1fa1e55725d79/packages/wrangler/src/config/validation.ts#L94-L98
117
+ return config.legacy_env;
118
+ }
119
+
91
120
  function getScriptName(
92
121
  args: { name: string | undefined; env: string | undefined },
93
122
  config: Config
94
123
  ): string | undefined {
95
- const shortScriptName = args.name ?? config.name;
96
- if (!shortScriptName) {
97
- return;
124
+ if (args.name && isLegacyEnv(config) && args.env) {
125
+ throw new CommandLineArgsError(
126
+ "In legacy environment mode you cannot use --name and --env together. If you want to specify a Worker name for a specific environment you can add the following to your wrangler.toml config:" +
127
+ `
128
+ [env.${args.env}]
129
+ name = "${args.name}"
130
+ `
131
+ );
98
132
  }
99
133
 
100
- return isLegacyEnv(args, config)
101
- ? `${shortScriptName}${args.env ? `-${args.env}` : ""}`
102
- : shortScriptName;
134
+ return args.name ?? config.name;
103
135
  }
104
136
 
105
- async function requireAuth(
106
- config: Config,
107
- isInteractive = true
108
- ): Promise<string> {
109
- const loggedIn = await loginOrRefreshIfRequired(isInteractive);
110
- if (!loggedIn) {
111
- // didn't login, let's just quit
112
- throw new Error("Did not login, quitting...");
113
- }
114
- const accountId = config.account_id || (await getAccountId(isInteractive));
115
- if (!accountId) {
116
- throw new Error("No account id found, quitting...");
117
- }
118
-
119
- return accountId;
137
+ /**
138
+ * Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
139
+ */
140
+ function getLegacyScriptName(
141
+ args: { name: string | undefined; env: string | undefined },
142
+ config: Config
143
+ ) {
144
+ return args.name && args.env && isLegacyEnv(config)
145
+ ? `${args.name}-${args.env}`
146
+ : args.name ?? config.name;
120
147
  }
121
148
 
122
149
  /**
@@ -178,21 +205,18 @@ function demandOneOfOption(...options: string[]) {
178
205
  }
179
206
 
180
207
  class CommandLineArgsError extends Error {}
181
- class DeprecationError extends Error {
182
- constructor(message: string) {
183
- super(`DEPRECATION WARNING:\n${message}`);
184
- }
185
- }
186
208
 
187
209
  export async function main(argv: string[]): Promise<void> {
188
210
  const wrangler = makeCLI(argv)
211
+ .strict()
189
212
  // We handle errors ourselves in a try-catch around `yargs.parse`.
190
213
  // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
191
214
  // Otherwise we just log the error that was thrown without any "help info".
192
215
  .showHelpOnFail(false)
193
216
  .fail((msg, error) => {
194
- if (!error) {
195
- // If there is only a `msg` then this came from yargs own validation, so wrap in a `CommandLineArgsError`.
217
+ if (!error || error.name === "YError") {
218
+ // If there is no error or the error is a "YError", then this came from yargs own validation
219
+ // Wrap it in a `CommandLineArgsError` so that we can handle it appropriately further up.
196
220
  error = new CommandLineArgsError(msg);
197
221
  }
198
222
  throw error;
@@ -200,7 +224,15 @@ export async function main(argv: string[]): Promise<void> {
200
224
  .scriptName("wrangler")
201
225
  .wrap(null);
202
226
 
203
- // the default is to simply print the help menu
227
+ // Default help command that supports the subcommands
228
+ const subHelp: CommandModule = {
229
+ command: ["*"],
230
+ handler: async (args) => {
231
+ setImmediate(() =>
232
+ wrangler.parse([...args._.map((a) => `${a}`), "--help"])
233
+ );
234
+ },
235
+ };
204
236
  wrangler.command(
205
237
  ["*"],
206
238
  false,
@@ -234,14 +266,19 @@ export async function main(argv: string[]): Promise<void> {
234
266
  default: "worker",
235
267
  })
236
268
  .positional("template", {
237
- describe: "a link to a GitHub template",
269
+ describe: "The URL of a GitHub template",
238
270
  default: "https://github.com/cloudflare/worker-template",
239
271
  });
240
272
  },
241
- () => {
273
+ (generateArgs) => {
242
274
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
243
275
  throw new DeprecationError(
244
- "`wrangler generate` has been deprecated, please refer to TODO://some/path for alternatives"
276
+ "`wrangler generate` has been deprecated.\n" +
277
+ "Try running `wrangler init` to generate a basic Worker, or cloning the template repository instead:\n\n" +
278
+ "```\n" +
279
+ `git clone ${generateArgs.template}\n` +
280
+ "```\n\n" +
281
+ "Please refer to https://developers.cloudflare.com/workers/wrangler/deprecations/#generate for more information."
245
282
  );
246
283
  }
247
284
  );
@@ -256,6 +293,18 @@ export async function main(argv: string[]): Promise<void> {
256
293
  describe: "The name of your worker",
257
294
  type: "string",
258
295
  })
296
+ .option("type", {
297
+ describe: "The type of worker to create",
298
+ type: "string",
299
+ choices: ["rust", "javascript", "webpack"],
300
+ hidden: true,
301
+ deprecated: true,
302
+ })
303
+ .option("site", {
304
+ hidden: true,
305
+ type: "boolean",
306
+ deprecated: true,
307
+ })
259
308
  .option("yes", {
260
309
  describe: 'Answer "yes" to any prompts for new projects',
261
310
  type: "boolean",
@@ -263,7 +312,8 @@ export async function main(argv: string[]): Promise<void> {
263
312
  });
264
313
  },
265
314
  async (args) => {
266
- if ("type" in args) {
315
+ await printWranglerBanner();
316
+ if (args.type) {
267
317
  let message = "The --type option is no longer supported.";
268
318
  if (args.type === "webpack") {
269
319
  message +=
@@ -273,9 +323,30 @@ export async function main(argv: string[]): Promise<void> {
273
323
  throw new CommandLineArgsError(message);
274
324
  }
275
325
 
276
- // TODO: make sure args.name is a valid identifier for a worker name
326
+ const creationDirectory = path.resolve(process.cwd(), args.name ?? "");
327
+
328
+ if (args.site) {
329
+ const gitDirectory =
330
+ creationDirectory !== process.cwd()
331
+ ? path.basename(creationDirectory)
332
+ : "my-site";
333
+ const message =
334
+ "The --site option is no longer supported.\n" +
335
+ "If you wish to create a brand new Worker Sites project then clone the `worker-sites-template` starter repository:\n\n" +
336
+ "```\n" +
337
+ `git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template ${gitDirectory}\n` +
338
+ `cd ${gitDirectory}\n` +
339
+ "```\n\n" +
340
+ "Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.\n" +
341
+ "Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/.";
342
+ throw new CommandLineArgsError(message);
343
+ }
277
344
 
278
- const creationDirectory = path.join(process.cwd(), args.name ?? "");
345
+ // TODO: make sure args.name is a valid identifier for a worker name
346
+ const workerName = path
347
+ .basename(creationDirectory)
348
+ .toLowerCase()
349
+ .replaceAll(/[^a-z0-9\-_]/gm, "-");
279
350
 
280
351
  const packageManager = await getPackageManager(creationDirectory);
281
352
 
@@ -287,10 +358,14 @@ export async function main(argv: string[]): Promise<void> {
287
358
  "./wrangler.toml"
288
359
  );
289
360
  let justCreatedWranglerToml = false;
290
- const workerName =
291
- args.name || path.basename(path.resolve(process.cwd()));
361
+
292
362
  if (fs.existsSync(wranglerTomlDestination)) {
293
- console.warn(`${wranglerTomlDestination} file already exists!`);
363
+ logger.warn(
364
+ `${path.relative(
365
+ process.cwd(),
366
+ wranglerTomlDestination
367
+ )} already exists!`
368
+ );
294
369
  const shouldContinue = await confirm(
295
370
  "Do you want to continue initializing this project?"
296
371
  );
@@ -300,6 +375,7 @@ export async function main(argv: string[]): Promise<void> {
300
375
  } else {
301
376
  await mkdir(creationDirectory, { recursive: true });
302
377
  const compatibilityDate = new Date().toISOString().substring(0, 10);
378
+
303
379
  try {
304
380
  await writeFile(
305
381
  wranglerTomlDestination,
@@ -308,18 +384,66 @@ export async function main(argv: string[]): Promise<void> {
308
384
  compatibility_date: compatibilityDate,
309
385
  }) + "\n"
310
386
  );
311
- console.log(`✨ Successfully created wrangler.toml`);
387
+
388
+ logger.log(
389
+ `✨ Created ${path.relative(
390
+ process.cwd(),
391
+ wranglerTomlDestination
392
+ )}`
393
+ );
312
394
  justCreatedWranglerToml = true;
313
395
  } catch (err) {
314
396
  throw new Error(
315
- `Failed to create wrangler.toml.\n${(err as Error).message ?? err}`
397
+ `Failed to create ${path.relative(
398
+ process.cwd(),
399
+ wranglerTomlDestination
400
+ )}.\n${(err as Error).message ?? err}`
316
401
  );
317
402
  }
318
403
  }
319
404
 
320
- let pathToPackageJson = await findUp("package.json");
321
- let shouldCreatePackageJson = false;
322
405
  const yesFlag = args.yes ?? false;
406
+
407
+ const isInsideGitProject = Boolean(
408
+ await findUp(".git", { cwd: creationDirectory, type: "directory" })
409
+ );
410
+ let isGitInstalled;
411
+ try {
412
+ isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
413
+ } catch (err) {
414
+ if ((err as { code: string | undefined }).code !== "ENOENT") {
415
+ // only throw if the error is not because git is not installed
416
+ throw err;
417
+ } else {
418
+ isGitInstalled = false;
419
+ }
420
+ }
421
+ if (!isInsideGitProject && isGitInstalled) {
422
+ const shouldInitGit =
423
+ yesFlag ||
424
+ (await confirm("Would you like to use git to manage this Worker?"));
425
+ if (shouldInitGit) {
426
+ await execa("git", ["init"], { cwd: creationDirectory });
427
+ await writeFile(
428
+ path.join(creationDirectory, ".gitignore"),
429
+ readFileSync(path.join(__dirname, "../templates/gitignore"))
430
+ );
431
+ logger.log(
432
+ args.name && args.name !== "."
433
+ ? `✨ Initialized git repository at ${path.relative(
434
+ process.cwd(),
435
+ creationDirectory
436
+ )}`
437
+ : `✨ Initialized git repository`
438
+ );
439
+ }
440
+ }
441
+
442
+ let pathToPackageJson = await findUp("package.json", {
443
+ cwd: creationDirectory,
444
+ });
445
+ let shouldCreatePackageJson = false;
446
+
323
447
  if (!pathToPackageJson) {
324
448
  // If no package.json exists, ask to create one
325
449
  shouldCreatePackageJson =
@@ -344,9 +468,12 @@ export async function main(argv: string[]): Promise<void> {
344
468
  " "
345
469
  ) + "\n"
346
470
  );
471
+
347
472
  await packageManager.install();
348
- console.log(`✨ Created package.json`);
349
473
  pathToPackageJson = path.join(creationDirectory, "package.json");
474
+ logger.log(
475
+ `✨ Created ${path.relative(process.cwd(), pathToPackageJson)}`
476
+ );
350
477
  } else {
351
478
  return;
352
479
  }
@@ -366,17 +493,22 @@ export async function main(argv: string[]): Promise<void> {
366
493
  const shouldInstall =
367
494
  yesFlag ||
368
495
  (await confirm(
369
- "Would you like to install wrangler into your package.json?"
496
+ `Would you like to install wrangler into ${path.relative(
497
+ process.cwd(),
498
+ pathToPackageJson
499
+ )}?`
370
500
  ));
371
501
  if (shouldInstall) {
372
502
  await packageManager.addDevDeps(`wrangler@${wranglerVersion}`);
373
- console.log(`✨ Installed wrangler`);
503
+ logger.log(`✨ Installed wrangler`);
374
504
  }
375
505
  }
376
506
  }
377
507
 
378
508
  let isTypescriptProject = false;
379
- let pathToTSConfig = await findUp("tsconfig.json");
509
+ let pathToTSConfig = await findUp("tsconfig.json", {
510
+ cwd: creationDirectory,
511
+ });
380
512
  if (!pathToTSConfig) {
381
513
  // If there's no tsconfig, offer to create one
382
514
  // and install @cloudflare/workers-types
@@ -390,11 +522,13 @@ export async function main(argv: string[]): Promise<void> {
390
522
  "@cloudflare/workers-types",
391
523
  "typescript"
392
524
  );
393
-
394
- console.log(
395
- `✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies`
396
- );
397
525
  pathToTSConfig = path.join(creationDirectory, "tsconfig.json");
526
+ logger.log(
527
+ `✨ Created ${path.relative(
528
+ process.cwd(),
529
+ pathToTSConfig
530
+ )}, installed @cloudflare/workers-types into devDependencies`
531
+ );
398
532
  }
399
533
  } else {
400
534
  isTypescriptProject = true;
@@ -419,8 +553,11 @@ export async function main(argv: string[]): Promise<void> {
419
553
  // it could be complicated in existing projects
420
554
  // and we don't want to break them. Instead, we simply
421
555
  // tell the user that they need to update their tsconfig.json
422
- console.log(
423
- `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in your tsconfig.json`
556
+ logger.log(
557
+ `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in ${path.relative(
558
+ process.cwd(),
559
+ pathToTSConfig
560
+ )}`
424
561
  );
425
562
  }
426
563
  }
@@ -443,7 +580,7 @@ export async function main(argv: string[]): Promise<void> {
443
580
  ) {
444
581
  if (isCreatingWranglerToml) {
445
582
  // rewrite wrangler.toml with main = "path/to/script"
446
- const parsedWranglerToml = TOML.parse(
583
+ const parsedWranglerToml = parseTOML(
447
584
  readFileSync(wranglerTomlDestination)
448
585
  );
449
586
  fs.writeFileSync(
@@ -478,21 +615,21 @@ export async function main(argv: string[]): Promise<void> {
478
615
  2
479
616
  ) + "\n"
480
617
  );
481
- console.log(
618
+ logger.log(
482
619
  `\nTo start developing your Worker, run \`${
483
620
  isNamedWorker ? `cd ${args.name} && ` : ""
484
621
  }npm start\``
485
622
  );
486
- console.log(
623
+ logger.log(
487
624
  `To publish your Worker to the Internet, run \`npm run publish\``
488
625
  );
489
626
  } else {
490
- console.log(
627
+ logger.log(
491
628
  `\nTo start developing your Worker, run \`npx wrangler dev\`${
492
629
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
493
630
  }`
494
631
  );
495
- console.log(
632
+ logger.log(
496
633
  `To publish your Worker to the Internet, run \`npx wrangler publish\`${
497
634
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
498
635
  }`
@@ -502,12 +639,13 @@ export async function main(argv: string[]): Promise<void> {
502
639
 
503
640
  if (isTypescriptProject) {
504
641
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.ts"))) {
505
- let shouldCreateSource = false;
506
-
507
- shouldCreateSource =
642
+ const shouldCreateSource =
508
643
  yesFlag ||
509
644
  (await confirm(
510
- `Would you like to create a Worker at src/index.ts?`
645
+ `Would you like to create a Worker at ${path.relative(
646
+ process.cwd(),
647
+ path.join(creationDirectory, "./src/index.ts")
648
+ )}?`
511
649
  ));
512
650
 
513
651
  if (shouldCreateSource) {
@@ -519,7 +657,12 @@ export async function main(argv: string[]): Promise<void> {
519
657
  readFileSync(path.join(__dirname, "../templates/new-worker.ts"))
520
658
  );
521
659
 
522
- console.log(`✨ Created src/index.ts`);
660
+ logger.log(
661
+ `✨ Created ${path.relative(
662
+ process.cwd(),
663
+ path.join(creationDirectory, "./src/index.ts")
664
+ )}`
665
+ );
523
666
 
524
667
  await writePackageJsonScriptsAndUpdateWranglerToml(
525
668
  shouldWritePackageJsonScripts,
@@ -530,20 +673,31 @@ export async function main(argv: string[]): Promise<void> {
530
673
  }
531
674
  }
532
675
  } else {
533
- if (!fs.existsSync("./src/index.js")) {
534
- const shouldCreateSource = await confirm(
535
- `Would you like to create a Worker at src/index.js?`
536
- );
676
+ if (!fs.existsSync(path.join(creationDirectory, "./src/index.js"))) {
677
+ const shouldCreateSource =
678
+ yesFlag ||
679
+ (await confirm(
680
+ `Would you like to create a Worker at ${path.relative(
681
+ process.cwd(),
682
+ path.join(creationDirectory, "./src/index.js")
683
+ )}?`
684
+ ));
685
+
537
686
  if (shouldCreateSource) {
538
687
  await mkdir(path.join(creationDirectory, "./src"), {
539
688
  recursive: true,
540
689
  });
541
690
  await writeFile(
542
- path.join(path.join(creationDirectory, "./src/index.js")),
691
+ path.join(creationDirectory, "./src/index.js"),
543
692
  readFileSync(path.join(__dirname, "../templates/new-worker.js"))
544
693
  );
545
694
 
546
- console.log(`✨ Created src/index.js`);
695
+ logger.log(
696
+ `✨ Created ${path.relative(
697
+ process.cwd(),
698
+ path.join(creationDirectory, "./src/index.js")
699
+ )}`
700
+ );
547
701
 
548
702
  await writePackageJsonScriptsAndUpdateWranglerToml(
549
703
  shouldWritePackageJsonScripts,
@@ -569,81 +723,11 @@ export async function main(argv: string[]): Promise<void> {
569
723
  () => {
570
724
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
571
725
  throw new DeprecationError(
572
- "`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
726
+ "`wrangler build` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives"
573
727
  );
574
728
  }
575
729
  );
576
730
 
577
- // login
578
- wrangler.command(
579
- // this needs scopes as an option?
580
- "login",
581
- false, // we don't need to show this in the menu
582
- // "🔓 Login to Cloudflare",
583
- (yargs) => {
584
- // TODO: This needs some copy editing
585
- // I mean, this entire app does, but this too.
586
- return yargs
587
- .option("scopes-list", {
588
- describe: "List all the available OAuth scopes with descriptions",
589
- })
590
- .option("scopes", {
591
- describe: "Pick the set of applicable OAuth scopes when logging in",
592
- array: true,
593
- type: "string",
594
- });
595
-
596
- // TODO: scopes
597
- },
598
- async (args) => {
599
- if (args["scopes-list"]) {
600
- listScopes();
601
- return;
602
- }
603
- if (args.scopes) {
604
- if (args.scopes.length === 0) {
605
- // don't allow no scopes to be passed, that would be weird
606
- listScopes();
607
- return;
608
- }
609
- if (!validateScopeKeys(args.scopes)) {
610
- throw new CommandLineArgsError(
611
- `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
612
- );
613
- }
614
- await login({ scopes: args.scopes });
615
- return;
616
- }
617
- await login();
618
-
619
- // TODO: would be nice if it optionally saved login
620
- // credentials inside node_modules/.cache or something
621
- // this way you could have multiple users on a single machine
622
- }
623
- );
624
-
625
- // logout
626
- wrangler.command(
627
- // this needs scopes as an option?
628
- "logout",
629
- false, // we don't need to show this in the menu
630
- // "🚪 Logout from Cloudflare",
631
- () => {},
632
- async () => {
633
- await logout();
634
- }
635
- );
636
-
637
- // whoami
638
- wrangler.command(
639
- "whoami",
640
- "🕵️ Retrieve your user info and test your auth config",
641
- () => {},
642
- async () => {
643
- await whoami();
644
- }
645
- );
646
-
647
731
  // config
648
732
  wrangler.command(
649
733
  "config",
@@ -652,7 +736,7 @@ export async function main(argv: string[]): Promise<void> {
652
736
  () => {
653
737
  // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
654
738
  throw new DeprecationError(
655
- "`wrangler config` has been deprecated, please refer to TODO://some/path for alternatives"
739
+ "`wrangler config` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#config for alternatives"
656
740
  );
657
741
  }
658
742
  );
@@ -670,23 +754,30 @@ export async function main(argv: string[]): Promise<void> {
670
754
  .option("name", {
671
755
  describe: "Name of the worker",
672
756
  type: "string",
757
+ requiresArg: true,
673
758
  })
674
759
  .option("format", {
675
760
  choices: ["modules", "service-worker"] as const,
676
761
  describe: "Choose an entry type",
762
+ deprecated: true,
677
763
  })
678
764
  .option("env", {
679
765
  describe: "Perform on a specific environment",
680
766
  type: "string",
767
+ requiresArg: true,
768
+ alias: "e",
681
769
  })
682
770
  .option("compatibility-date", {
683
771
  describe: "Date to use for compatibility checks",
684
772
  type: "string",
773
+ requiresArg: true,
685
774
  })
686
775
  .option("compatibility-flags", {
687
776
  describe: "Flags to use for compatibility checks",
688
- type: "array",
689
777
  alias: "compatibility-flag",
778
+ type: "string",
779
+ requiresArg: true,
780
+ array: true,
690
781
  })
691
782
  .option("latest", {
692
783
  describe: "Use the latest version of the worker runtime",
@@ -694,9 +785,9 @@ export async function main(argv: string[]): Promise<void> {
694
785
  default: true,
695
786
  })
696
787
  .option("ip", {
697
- describe: "IP address to listen on",
788
+ describe: "IP address to listen on, defaults to `localhost`",
698
789
  type: "string",
699
- default: "127.0.0.1",
790
+ requiresArg: true,
700
791
  })
701
792
  .option("port", {
702
793
  describe: "Port to listen on",
@@ -709,51 +800,63 @@ export async function main(argv: string[]): Promise<void> {
709
800
  .option("routes", {
710
801
  describe: "Routes to upload",
711
802
  alias: "route",
712
- type: "array",
803
+ type: "string",
804
+ requiresArg: true,
805
+ array: true,
713
806
  })
714
807
  .option("host", {
715
808
  type: "string",
809
+ requiresArg: true,
716
810
  describe:
717
811
  "Host to forward requests to, defaults to the zone of project",
718
812
  })
719
813
  .option("local-protocol", {
720
- default: "http",
721
814
  describe: "Protocol to listen to requests on, defaults to http.",
722
- choices: ["http", "https"],
815
+ choices: ["http", "https"] as const,
723
816
  })
724
817
  .option("experimental-public", {
725
818
  describe: "Static assets to be served",
726
819
  type: "string",
820
+ requiresArg: true,
727
821
  })
728
822
  .option("site", {
729
823
  describe: "Root folder of static assets for Workers Sites",
730
824
  type: "string",
825
+ requiresArg: true,
731
826
  })
732
827
  .option("site-include", {
733
828
  describe:
734
829
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
735
830
  type: "string",
831
+ requiresArg: true,
736
832
  array: true,
737
833
  })
738
834
  .option("site-exclude", {
739
835
  describe:
740
836
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
741
837
  type: "string",
838
+ requiresArg: true,
742
839
  array: true,
743
840
  })
744
841
  .option("upstream-protocol", {
745
- default: "https",
746
842
  describe:
747
843
  "Protocol to forward requests to host on, defaults to https.",
748
- choices: ["http", "https"],
844
+ choices: ["http", "https"] as const,
749
845
  })
750
846
  .option("jsx-factory", {
751
847
  describe: "The function that is called for each JSX element",
752
848
  type: "string",
849
+ requiresArg: true,
753
850
  })
754
851
  .option("jsx-fragment", {
755
852
  describe: "The function that is called for each JSX fragment",
756
853
  type: "string",
854
+ requiresArg: true,
855
+ })
856
+ .option("tsconfig", {
857
+ describe: "Path to a custom tsconfig.json file",
858
+ type: "string",
859
+ requiresArg: true,
757
860
  })
758
861
  .option("local", {
759
862
  alias: "l",
@@ -761,35 +864,62 @@ export async function main(argv: string[]): Promise<void> {
761
864
  type: "boolean",
762
865
  default: false, // I bet this will a point of contention. We'll revisit it.
763
866
  })
867
+ .option("minify", {
868
+ describe: "Minify the script",
869
+ type: "boolean",
870
+ })
871
+ .option("node-compat", {
872
+ describe: "Enable node.js compatibility",
873
+ type: "boolean",
874
+ })
764
875
  .option("experimental-enable-local-persistence", {
765
876
  describe: "Enable persistence for this session (only for local mode)",
766
877
  type: "boolean",
878
+ })
879
+ .option("inspect", {
880
+ describe: "Enable dev tools",
881
+ type: "boolean",
882
+ deprecated: true,
883
+ })
884
+ .option("legacy-env", {
885
+ type: "boolean",
886
+ describe: "Use legacy environments",
887
+ hidden: true,
767
888
  });
768
889
  },
769
890
  async (args) => {
770
- const config = readConfig(
891
+ await printWranglerBanner();
892
+ const configPath =
771
893
  (args.config as ConfigPath) ||
772
- (args.script && findWranglerToml(path.dirname(args.script)))
773
- );
774
- const entry = getEntry(config, "dev", args.script);
894
+ (args.script && findWranglerToml(path.dirname(args.script)));
895
+ const config = readConfig(configPath, args);
896
+ const entry = await getEntry(args, config, "dev");
897
+
898
+ if (args.inspect) {
899
+ logger.warn(
900
+ "Passing --inspect is unnecessary, now you can always connect to devtools."
901
+ );
902
+ }
775
903
 
776
904
  if (args["experimental-public"]) {
777
- console.warn(
778
- "🚨 The --experimental-public field is experimental and will change in the future."
905
+ logger.warn(
906
+ "The --experimental-public field is experimental and will change in the future."
779
907
  );
780
908
  }
781
909
 
782
910
  if (args.public) {
783
911
  throw new Error(
784
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
912
+ "The --public field has been renamed to --experimental-public, and will change behaviour in the future."
785
913
  );
786
914
  }
787
915
 
788
- if (config.site?.["entry-point"]) {
789
- console.warn(
790
- "Deprecation notice: The `site.entry-point` config field is no longer used.\n" +
791
- "The entry-point is specified via the command line (e.g. `wrangler dev path/to/script`).\n" +
792
- "Please remove the `site.entry-point` field from the `wrangler.toml` file."
916
+ const upstreamProtocol =
917
+ args["upstream-protocol"] || config.dev.upstream_protocol;
918
+ if (upstreamProtocol === "http") {
919
+ logger.warn(
920
+ "Setting upstream-protocol to http is not currently implemented.\n" +
921
+ "If this is required in your project, please add your use case to the following issue:\n" +
922
+ "https://github.com/cloudflare/wrangler2/issues/583."
793
923
  );
794
924
  }
795
925
 
@@ -823,16 +953,6 @@ export async function main(argv: string[]): Promise<void> {
823
953
  return zones[0]?.id;
824
954
  }
825
955
 
826
- const environments = config.env ?? {};
827
- const envRootObj = args.env ? environments[args.env] || {} : config;
828
-
829
- const hostLike =
830
- args.host ||
831
- config.dev.host ||
832
- (args.routes && args.routes[0]) ||
833
- envRootObj.route ||
834
- (envRootObj.routes && envRootObj.routes[0]);
835
-
836
956
  // When we're given a host (in one of the above ways), we do 2 things:
837
957
  // - We try to extract a host from it
838
958
  // - We try to get a zone id from the host
@@ -843,27 +963,57 @@ export async function main(argv: string[]): Promise<void> {
843
963
  // get a zone id for it, by lopping off subdomains until we get a hit
844
964
  // from the API. That's it!
845
965
 
846
- const host = typeof hostLike === "string" ? getHost(hostLike) : undefined;
966
+ let zone: { host: string; id: string } | undefined;
967
+
968
+ if (!args.local) {
969
+ const hostLike =
970
+ args.host ||
971
+ config.dev.host ||
972
+ (args.routes && args.routes[0]) ||
973
+ config.route ||
974
+ (config.routes && config.routes[0]);
975
+
976
+ let zoneId: string | undefined =
977
+ typeof hostLike === "object" && "zone_id" in hostLike
978
+ ? hostLike.zone_id
979
+ : undefined;
980
+
981
+ const host =
982
+ typeof hostLike === "string"
983
+ ? getHost(hostLike)
984
+ : typeof hostLike === "object"
985
+ ? "zone_name" in hostLike
986
+ ? getHost(hostLike.zone_name)
987
+ : getHost(hostLike.pattern)
988
+ : undefined;
989
+
990
+ const hostPieces =
991
+ typeof host === "string" ? host.split(".") : undefined;
992
+
993
+ while (!zoneId && hostPieces && hostPieces.length > 1) {
994
+ zoneId = await getZoneId(hostPieces.join("."));
995
+ hostPieces.shift();
996
+ }
847
997
 
848
- let zoneId: string | undefined;
849
- const hostPieces = typeof host === "string" ? host.split(".") : undefined;
998
+ if (host && !zoneId) {
999
+ throw new Error(`Could not find zone for ${hostLike}`);
1000
+ }
850
1001
 
851
- while (hostPieces && hostPieces.length > 1) {
852
- zoneId = await getZoneId(hostPieces.join("."));
853
- if (zoneId) break;
854
- hostPieces.shift();
855
- }
856
- if (host && !zoneId) {
857
- throw new Error(`Could not find zone for ${hostLike}`);
1002
+ zone =
1003
+ typeof zoneId === "string" && typeof host === "string"
1004
+ ? {
1005
+ host,
1006
+ id: zoneId,
1007
+ }
1008
+ : undefined;
858
1009
  }
859
1010
 
860
- const zone =
861
- typeof zoneId === "string" && typeof host === "string"
862
- ? {
863
- host,
864
- id: zoneId,
865
- }
866
- : undefined;
1011
+ const nodeCompat = args.nodeCompat ?? config.node_compat;
1012
+ if (nodeCompat) {
1013
+ logger.warn(
1014
+ "Enabling node.js compatibility mode for built-ins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
1015
+ );
1016
+ }
867
1017
 
868
1018
  const { waitUntilExit } = render(
869
1019
  <Dev
@@ -872,12 +1022,16 @@ export async function main(argv: string[]): Promise<void> {
872
1022
  env={args.env}
873
1023
  zone={zone}
874
1024
  rules={getRules(config)}
875
- legacyEnv={isLegacyEnv(args, config)}
876
- buildCommand={config.build || {}}
877
- format={args.format || config.build?.upload?.format}
1025
+ legacyEnv={isLegacyEnv(config)}
1026
+ minify={args.minify ?? config.minify}
1027
+ nodeCompat={nodeCompat}
1028
+ build={config.build || {}}
878
1029
  initialMode={args.local ? "local" : "remote"}
879
- jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
880
- jsxFragment={args["jsx-fragment"] || envRootObj?.jsx_fragment}
1030
+ jsxFactory={args["jsx-factory"] || config.jsx_factory}
1031
+ jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
1032
+ tsconfig={args.tsconfig ?? config.tsconfig}
1033
+ upstreamProtocol={upstreamProtocol}
1034
+ localProtocol={args["local-protocol"] || config.dev.local_protocol}
881
1035
  enableLocalPersistence={
882
1036
  args["experimental-enable-local-persistence"] || false
883
1037
  }
@@ -889,24 +1043,25 @@ export async function main(argv: string[]): Promise<void> {
889
1043
  args.siteExclude
890
1044
  )}
891
1045
  port={
892
- args.port || config.dev?.port || (await getPort({ port: 8787 }))
1046
+ args.port ||
1047
+ config.dev.port ||
1048
+ (await getPort({ port: DEFAULT_LOCAL_PORT }))
893
1049
  }
1050
+ ip={args.ip || config.dev.ip}
894
1051
  inspectorPort={
895
1052
  args["inspector-port"] ?? (await getPort({ port: 9229 }))
896
1053
  }
897
1054
  public={args["experimental-public"]}
898
- compatibilityDate={
899
- args["compatibility-date"] ||
900
- config.compatibility_date ||
901
- new Date().toISOString().substring(0, 10)
902
- }
1055
+ compatibilityDate={getDevCompatibilityDate(
1056
+ config,
1057
+ args["compatibility-date"]
1058
+ )}
903
1059
  compatibilityFlags={
904
- (args["compatibility-flags"] as string[]) ||
905
- config.compatibility_flags
1060
+ args["compatibility-flags"] || config.compatibility_flags
906
1061
  }
907
- usageModel={envRootObj.usage_model}
1062
+ usageModel={config.usage_model}
908
1063
  bindings={{
909
- kv_namespaces: envRootObj.kv_namespaces?.map(
1064
+ kv_namespaces: config.kv_namespaces?.map(
910
1065
  ({ binding, preview_id, id: _id }) => {
911
1066
  // In `dev`, we make folks use a separate kv namespace called
912
1067
  // `preview_id` instead of `id` so that they don't
@@ -927,13 +1082,29 @@ export async function main(argv: string[]): Promise<void> {
927
1082
  };
928
1083
  }
929
1084
  ),
930
- vars: envRootObj.vars,
1085
+ vars: getVarsForDev(config),
931
1086
  wasm_modules: config.wasm_modules,
932
1087
  text_blobs: config.text_blobs,
933
- durable_objects: envRootObj.durable_objects,
934
- r2_buckets: envRootObj.r2_buckets,
935
- unsafe: envRootObj.unsafe?.bindings,
1088
+ data_blobs: config.data_blobs,
1089
+ durable_objects: config.durable_objects,
1090
+ r2_buckets: config.r2_buckets?.map(
1091
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1092
+ // same idea as kv namespace preview id,
1093
+ // same copy-on-write TODO
1094
+ if (!preview_bucket_name) {
1095
+ throw new Error(
1096
+ `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
1097
+ );
1098
+ }
1099
+ return {
1100
+ binding,
1101
+ bucket_name: preview_bucket_name,
1102
+ };
1103
+ }
1104
+ ),
1105
+ unsafe: config.unsafe?.bindings,
936
1106
  }}
1107
+ crons={config.triggers.crons}
937
1108
  />
938
1109
  );
939
1110
  await waitUntilExit();
@@ -948,28 +1119,41 @@ export async function main(argv: string[]): Promise<void> {
948
1119
  return yargs
949
1120
  .option("env", {
950
1121
  type: "string",
1122
+ requiresArg: true,
951
1123
  describe: "Perform on a specific environment",
1124
+ alias: "e",
952
1125
  })
953
1126
  .positional("script", {
954
1127
  describe: "The path to an entry point for your worker",
955
1128
  type: "string",
1129
+ requiresArg: true,
956
1130
  })
957
1131
  .option("name", {
958
1132
  describe: "Name of the worker",
959
1133
  type: "string",
1134
+ requiresArg: true,
1135
+ })
1136
+ .option("outdir", {
1137
+ describe: "Output directory for the bundled worker",
1138
+ type: "string",
1139
+ requiresArg: true,
960
1140
  })
961
1141
  .option("format", {
962
1142
  choices: ["modules", "service-worker"] as const,
963
1143
  describe: "Choose an entry type",
1144
+ deprecated: true,
964
1145
  })
965
1146
  .option("compatibility-date", {
966
1147
  describe: "Date to use for compatibility checks",
967
1148
  type: "string",
1149
+ requiresArg: true,
968
1150
  })
969
1151
  .option("compatibility-flags", {
970
1152
  describe: "Flags to use for compatibility checks",
971
- type: "array",
972
1153
  alias: "compatibility-flag",
1154
+ type: "string",
1155
+ requiresArg: true,
1156
+ array: true,
973
1157
  })
974
1158
  .option("latest", {
975
1159
  describe: "Use the latest version of the worker runtime",
@@ -979,63 +1163,96 @@ export async function main(argv: string[]): Promise<void> {
979
1163
  .option("experimental-public", {
980
1164
  describe: "Static assets to be served",
981
1165
  type: "string",
1166
+ requiresArg: true,
982
1167
  })
983
1168
  .option("site", {
984
1169
  describe: "Root folder of static assets for Workers Sites",
985
1170
  type: "string",
1171
+ requiresArg: true,
986
1172
  })
987
1173
  .option("site-include", {
988
1174
  describe:
989
1175
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
990
1176
  type: "string",
1177
+ requiresArg: true,
991
1178
  array: true,
992
1179
  })
993
1180
  .option("site-exclude", {
994
1181
  describe:
995
1182
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
996
1183
  type: "string",
1184
+ requiresArg: true,
997
1185
  array: true,
998
1186
  })
999
1187
  .option("triggers", {
1000
1188
  describe: "cron schedules to attach",
1001
1189
  alias: ["schedule", "schedules"],
1002
- type: "array",
1190
+ type: "string",
1191
+ requiresArg: true,
1192
+ array: true,
1003
1193
  })
1004
1194
  .option("routes", {
1005
1195
  describe: "Routes to upload",
1006
1196
  alias: "route",
1007
- type: "array",
1197
+ type: "string",
1198
+ requiresArg: true,
1199
+ array: true,
1008
1200
  })
1009
1201
  .option("jsx-factory", {
1010
1202
  describe: "The function that is called for each JSX element",
1011
1203
  type: "string",
1204
+ requiresArg: true,
1012
1205
  })
1013
1206
  .option("jsx-fragment", {
1014
1207
  describe: "The function that is called for each JSX fragment",
1015
1208
  type: "string",
1209
+ requiresArg: true,
1210
+ })
1211
+ .option("tsconfig", {
1212
+ describe: "Path to a custom tsconfig.json file",
1213
+ type: "string",
1214
+ requiresArg: true,
1215
+ })
1216
+ .option("minify", {
1217
+ describe: "Minify the script",
1218
+ type: "boolean",
1219
+ })
1220
+ .option("node-compat", {
1221
+ describe: "Enable node.js compatibility",
1222
+ type: "boolean",
1223
+ })
1224
+ .option("dry-run", {
1225
+ describe: "Don't actually publish",
1226
+ type: "boolean",
1227
+ })
1228
+ .option("legacy-env", {
1229
+ type: "boolean",
1230
+ describe: "Use legacy environments",
1231
+ hidden: true,
1016
1232
  });
1017
1233
  },
1018
1234
  async (args) => {
1235
+ await printWranglerBanner();
1019
1236
  if (args["experimental-public"]) {
1020
- console.warn(
1021
- "🚨 The --experimental-public field is experimental and will change in the future."
1237
+ logger.warn(
1238
+ "The --experimental-public field is experimental and will change in the future."
1022
1239
  );
1023
1240
  }
1024
1241
  if (args.public) {
1025
1242
  throw new Error(
1026
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
1243
+ "The --public field has been renamed to --experimental-public, and will change behaviour in the future."
1027
1244
  );
1028
1245
  }
1029
1246
 
1030
- const config = readConfig(
1247
+ const configPath =
1031
1248
  (args.config as ConfigPath) ||
1032
- (args.script && findWranglerToml(path.dirname(args.script)))
1033
- );
1034
- const entry = getEntry(config, "publish", args.script);
1249
+ (args.script && findWranglerToml(path.dirname(args.script)));
1250
+ const config = readConfig(configPath, args);
1251
+ const entry = await getEntry(args, config, "publish");
1035
1252
 
1036
1253
  if (args.latest) {
1037
- console.warn(
1038
- "⚠️ Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
1254
+ logger.warn(
1255
+ "Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
1039
1256
  );
1040
1257
  }
1041
1258
 
@@ -1052,20 +1269,24 @@ export async function main(argv: string[]): Promise<void> {
1052
1269
  accountId,
1053
1270
  name: getScriptName(args, config),
1054
1271
  rules: getRules(config),
1055
- format: args.format || config.build?.upload?.format,
1056
1272
  entry,
1057
1273
  env: args.env,
1058
1274
  compatibilityDate: args.latest
1059
1275
  ? new Date().toISOString().substring(0, 10)
1060
1276
  : args["compatibility-date"],
1061
- compatibilityFlags: args["compatibility-flags"] as string[],
1277
+ compatibilityFlags: args["compatibility-flags"],
1062
1278
  triggers: args.triggers,
1063
1279
  jsxFactory: args["jsx-factory"],
1064
1280
  jsxFragment: args["jsx-fragment"],
1281
+ tsconfig: args.tsconfig,
1065
1282
  routes: args.routes,
1066
1283
  assetPaths,
1067
- legacyEnv: isLegacyEnv(args, config),
1284
+ legacyEnv: isLegacyEnv(config),
1285
+ minify: args.minify,
1286
+ nodeCompat: args.nodeCompat,
1068
1287
  experimentalPublic: args["experimental-public"] !== undefined,
1288
+ outDir: args.outdir,
1289
+ dryRun: args.dryRun,
1069
1290
  });
1070
1291
  }
1071
1292
  );
@@ -1075,63 +1296,74 @@ export async function main(argv: string[]): Promise<void> {
1075
1296
  "tail [name]",
1076
1297
  "🦚 Starts a log tailing session for a published Worker.",
1077
1298
  (yargs) => {
1078
- return (
1079
- yargs
1080
- .positional("name", {
1081
- describe: "Name of the worker",
1082
- type: "string",
1083
- })
1084
- // TODO: auto-detect if this should be json or pretty based on atty
1085
- .option("format", {
1086
- default: "json",
1087
- choices: ["json", "pretty"],
1088
- describe: "The format of log entries",
1089
- })
1090
- .option("status", {
1091
- choices: ["ok", "error", "canceled"],
1092
- describe: "Filter by invocation status",
1093
- array: true,
1094
- })
1095
- .option("header", {
1096
- type: "string",
1097
- describe: "Filter by HTTP header",
1098
- })
1099
- .option("method", {
1100
- type: "string",
1101
- describe: "Filter by HTTP method",
1102
- array: true,
1103
- })
1104
- .option("sampling-rate", {
1105
- type: "number",
1106
- describe: "Adds a percentage of requests to log sampling rate",
1107
- })
1108
- .option("search", {
1109
- type: "string",
1110
- describe: "Filter by a text match in console.log messages",
1111
- })
1112
- .option("ip", {
1113
- type: "string",
1114
- describe:
1115
- 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
1116
- array: true,
1117
- })
1118
- .option("env", {
1119
- type: "string",
1120
- describe: "Perform on a specific environment",
1121
- })
1122
- .option("debug", {
1123
- type: "boolean",
1124
- hidden: true,
1125
- default: false,
1126
- describe:
1127
- "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1128
- })
1129
- );
1299
+ return yargs
1300
+ .positional("name", {
1301
+ describe: "Name of the worker",
1302
+ type: "string",
1303
+ })
1304
+ .option("format", {
1305
+ default: process.stdout.isTTY ? "pretty" : "json",
1306
+ choices: ["json", "pretty"],
1307
+ describe: "The format of log entries",
1308
+ })
1309
+ .option("status", {
1310
+ choices: ["ok", "error", "canceled"],
1311
+ describe: "Filter by invocation status",
1312
+ array: true,
1313
+ })
1314
+ .option("header", {
1315
+ type: "string",
1316
+ requiresArg: true,
1317
+ describe: "Filter by HTTP header",
1318
+ })
1319
+ .option("method", {
1320
+ type: "string",
1321
+ requiresArg: true,
1322
+ describe: "Filter by HTTP method",
1323
+ array: true,
1324
+ })
1325
+ .option("sampling-rate", {
1326
+ type: "number",
1327
+ describe: "Adds a percentage of requests to log sampling rate",
1328
+ })
1329
+ .option("search", {
1330
+ type: "string",
1331
+ requiresArg: true,
1332
+ describe: "Filter by a text match in console.log messages",
1333
+ })
1334
+ .option("ip", {
1335
+ type: "string",
1336
+ requiresArg: true,
1337
+ describe:
1338
+ 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
1339
+ array: true,
1340
+ })
1341
+ .option("env", {
1342
+ type: "string",
1343
+ requiresArg: true,
1344
+ describe: "Perform on a specific environment",
1345
+ alias: "e",
1346
+ })
1347
+ .option("debug", {
1348
+ type: "boolean",
1349
+ hidden: true,
1350
+ default: false,
1351
+ describe:
1352
+ "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1353
+ })
1354
+ .option("legacy-env", {
1355
+ type: "boolean",
1356
+ describe: "Use legacy environments",
1357
+ hidden: true,
1358
+ });
1130
1359
  },
1131
1360
  async (args) => {
1132
- const config = readConfig(args.config as ConfigPath);
1361
+ if (args.format === "pretty") {
1362
+ await printWranglerBanner();
1363
+ }
1364
+ const config = readConfig(args.config as ConfigPath, args);
1133
1365
 
1134
- const scriptName = getScriptName(args, config);
1366
+ const scriptName = getLegacyScriptName(args, config);
1135
1367
 
1136
1368
  if (!scriptName) {
1137
1369
  throw new Error("Missing script name");
@@ -1140,7 +1372,7 @@ export async function main(argv: string[]): Promise<void> {
1140
1372
  const accountId = await requireAuth(config);
1141
1373
 
1142
1374
  const cliFilters: TailCLIFilters = {
1143
- status: args.status as ("ok" | "error" | "canceled")[],
1375
+ status: args.status as ("ok" | "error" | "canceled")[] | undefined,
1144
1376
  header: args.header,
1145
1377
  method: args.method,
1146
1378
  samplingRate: args["sampling-rate"],
@@ -1157,16 +1389,18 @@ export async function main(argv: string[]): Promise<void> {
1157
1389
  accountId,
1158
1390
  scriptName,
1159
1391
  filters,
1160
- !isLegacyEnv(args, config) ? args.env : undefined
1392
+ !isLegacyEnv(config) ? args.env : undefined
1161
1393
  );
1162
1394
 
1163
1395
  const scriptDisplayName = `${scriptName}${
1164
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1396
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1165
1397
  }`;
1166
1398
 
1167
- console.log(
1168
- `successfully created tail, expires at ${expiration.toLocaleString()}`
1169
- );
1399
+ if (args.format === "pretty") {
1400
+ logger.log(
1401
+ `Successfully created tail, expires at ${expiration.toLocaleString()}`
1402
+ );
1403
+ }
1170
1404
 
1171
1405
  onExit(async () => {
1172
1406
  tail.terminate();
@@ -1193,7 +1427,9 @@ export async function main(argv: string[]): Promise<void> {
1193
1427
  }
1194
1428
  }
1195
1429
 
1196
- console.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1430
+ if (args.format === "pretty") {
1431
+ logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1432
+ }
1197
1433
 
1198
1434
  tail.on("close", async () => {
1199
1435
  tail.terminate();
@@ -1218,6 +1454,7 @@ export async function main(argv: string[]): Promise<void> {
1218
1454
  })
1219
1455
  .option("env", {
1220
1456
  type: "string",
1457
+ requiresArg: true,
1221
1458
  describe: "Perform on a specific environment",
1222
1459
  })
1223
1460
  .option("watch", {
@@ -1235,21 +1472,18 @@ export async function main(argv: string[]): Promise<void> {
1235
1472
  }
1236
1473
 
1237
1474
  // Delegate to `wrangler dev`
1238
- console.warn(
1475
+ logger.warn(
1239
1476
  "***************************************************\n" +
1240
1477
  "The `wrangler preview` command has been deprecated.\n" +
1241
1478
  "Attempting to run `wrangler dev` instead.\n" +
1242
1479
  "***************************************************\n"
1243
1480
  );
1244
1481
 
1245
- const config = readConfig(args.config as ConfigPath);
1246
- const entry = getEntry(config, "dev");
1482
+ const config = readConfig(args.config as ConfigPath, args);
1483
+ const entry = await getEntry({}, config, "dev");
1247
1484
 
1248
1485
  const accountId = await requireAuth(config);
1249
1486
 
1250
- const environments = config.env ?? {};
1251
- const envRootObj = args.env ? environments[args.env] || {} : config;
1252
-
1253
1487
  const { waitUntilExit } = render(
1254
1488
  <Dev
1255
1489
  name={config.name}
@@ -1257,28 +1491,29 @@ export async function main(argv: string[]): Promise<void> {
1257
1491
  rules={getRules(config)}
1258
1492
  env={args.env}
1259
1493
  zone={undefined}
1260
- legacyEnv={isLegacyEnv(args, config)}
1261
- buildCommand={config.build || {}}
1262
- format={config.build?.upload?.format}
1494
+ legacyEnv={isLegacyEnv(config)}
1495
+ build={config.build || {}}
1496
+ minify={undefined}
1497
+ nodeCompat={config.node_compat}
1263
1498
  initialMode={args.local ? "local" : "remote"}
1264
- jsxFactory={envRootObj?.jsx_factory}
1265
- jsxFragment={envRootObj?.jsx_fragment}
1499
+ jsxFactory={config.jsx_factory}
1500
+ jsxFragment={config.jsx_fragment}
1501
+ tsconfig={config.tsconfig}
1502
+ upstreamProtocol={config.dev.upstream_protocol}
1503
+ localProtocol={config.dev.local_protocol}
1266
1504
  enableLocalPersistence={false}
1267
1505
  accountId={accountId}
1268
1506
  assetPaths={undefined}
1269
- port={config.dev?.port}
1270
- public={undefined}
1271
- compatibilityDate={
1272
- config.compatibility_date ||
1273
- new Date().toISOString().substring(0, 10)
1274
- }
1275
- compatibilityFlags={
1276
- (args["compatibility-flags"] as string[]) ||
1277
- config.compatibility_flags
1507
+ port={
1508
+ config.dev.port || (await getPort({ port: DEFAULT_LOCAL_PORT }))
1278
1509
  }
1510
+ ip={config.dev.ip}
1511
+ public={undefined}
1512
+ compatibilityDate={getDevCompatibilityDate(config)}
1513
+ compatibilityFlags={config.compatibility_flags}
1279
1514
  usageModel={config.usage_model}
1280
1515
  bindings={{
1281
- kv_namespaces: envRootObj.kv_namespaces?.map(
1516
+ kv_namespaces: config.kv_namespaces?.map(
1282
1517
  ({ binding, preview_id, id: _id }) => {
1283
1518
  // In `dev`, we make folks use a separate kv namespace called
1284
1519
  // `preview_id` instead of `id` so that they don't
@@ -1299,13 +1534,29 @@ export async function main(argv: string[]): Promise<void> {
1299
1534
  };
1300
1535
  }
1301
1536
  ),
1302
- vars: envRootObj.vars,
1537
+ vars: config.vars,
1303
1538
  wasm_modules: config.wasm_modules,
1304
1539
  text_blobs: config.text_blobs,
1305
- durable_objects: envRootObj.durable_objects,
1306
- r2_buckets: envRootObj.r2_buckets,
1307
- unsafe: envRootObj.unsafe?.bindings,
1540
+ data_blobs: config.data_blobs,
1541
+ durable_objects: config.durable_objects,
1542
+ r2_buckets: config.r2_buckets?.map(
1543
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1544
+ // same idea as kv namespace preview id,
1545
+ // same copy-on-write TODO
1546
+ if (!preview_bucket_name) {
1547
+ throw new Error(
1548
+ `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
1549
+ );
1550
+ }
1551
+ return {
1552
+ binding,
1553
+ bucket_name: preview_bucket_name,
1554
+ };
1555
+ }
1556
+ ),
1557
+ unsafe: config.unsafe?.bindings,
1308
1558
  }}
1559
+ crons={config.triggers.crons}
1309
1560
  inspectorPort={await getPort({ port: 9229 })}
1310
1561
  />
1311
1562
  );
@@ -1313,7 +1564,7 @@ export async function main(argv: string[]): Promise<void> {
1313
1564
  }
1314
1565
  );
1315
1566
 
1316
- // route
1567
+ // [DEPRECATED] route
1317
1568
  wrangler.command(
1318
1569
  "route",
1319
1570
  false, // I think we want to hide this command
@@ -1327,10 +1578,12 @@ export async function main(argv: string[]): Promise<void> {
1327
1578
  return yargs
1328
1579
  .option("env", {
1329
1580
  type: "string",
1581
+ requiresArg: true,
1330
1582
  describe: "Perform on a specific environment",
1331
1583
  })
1332
1584
  .option("zone", {
1333
1585
  type: "string",
1586
+ requiresArg: true,
1334
1587
  describe: "Zone id",
1335
1588
  })
1336
1589
  .positional("zone", {
@@ -1338,21 +1591,21 @@ export async function main(argv: string[]): Promise<void> {
1338
1591
  type: "string",
1339
1592
  });
1340
1593
  },
1341
- async (args) => {
1342
- console.log(":route list", args);
1343
- // TODO: use environment (current wrangler doesn't do so?)
1344
- // TODO: don't get `zone_id` from config at all and require the command line arg `--zone`.
1345
- const config = readConfig(args.config as ConfigPath);
1346
- const zone = args.zone || config.zone_id;
1347
- if (!zone) {
1348
- throw new Error("missing zone id");
1349
- }
1350
-
1351
- console.log(await fetchResult(`/zones/${zone}/workers/routes`));
1594
+ () => {
1595
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1596
+ const deprecationNotice =
1597
+ "`wrangler route list` has been deprecated.";
1598
+ const futureRoutes =
1599
+ "Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.";
1600
+ const presentRoutes =
1601
+ "Refer to the Cloudflare Dashboard to see the routes this worker is currently running on.";
1602
+ throw new DeprecationError(
1603
+ `${deprecationNotice}\n${futureRoutes}\n${presentRoutes}`
1604
+ );
1352
1605
  }
1353
1606
  )
1354
1607
  .command(
1355
- "delete <id>",
1608
+ "delete [id]",
1356
1609
  "Delete a route associated with a zone",
1357
1610
  (yargs) => {
1358
1611
  return yargs
@@ -1362,29 +1615,31 @@ export async function main(argv: string[]): Promise<void> {
1362
1615
  })
1363
1616
  .option("zone", {
1364
1617
  type: "string",
1618
+ requiresArg: true,
1365
1619
  describe: "zone id",
1366
1620
  })
1367
1621
  .option("env", {
1368
1622
  type: "string",
1623
+ requiresArg: true,
1369
1624
  describe: "Perform on a specific environment",
1370
1625
  });
1371
1626
  },
1372
- async (args) => {
1373
- console.log(":route delete", args);
1374
- // TODO: use environment (current wrangler doesn't do so?)
1375
- const config = readConfig(args.config as ConfigPath);
1376
- const zone = args.zone || config.zone_id;
1377
- if (!zone) {
1378
- throw new Error("missing zone id");
1379
- }
1380
-
1381
- console.log(
1382
- await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
1383
- method: "DELETE",
1384
- })
1385
- );
1627
+ () => {
1628
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1629
+ const deprecationNotice =
1630
+ "`wrangler route delete` has been deprecated.";
1631
+ const shouldDo =
1632
+ "Remove the unwanted route(s) from wrangler.toml and run `wrangler publish` to remove your worker from those routes.";
1633
+ throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
1386
1634
  }
1387
1635
  );
1636
+ },
1637
+ () => {
1638
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1639
+ const deprecationNotice = "`wrangler route` has been deprecated.";
1640
+ const shouldDo =
1641
+ "Please use wrangler.toml and/or `wrangler publish --routes` to modify routes";
1642
+ throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
1388
1643
  }
1389
1644
  );
1390
1645
 
@@ -1398,7 +1653,7 @@ export async function main(argv: string[]): Promise<void> {
1398
1653
  },
1399
1654
  () => {
1400
1655
  throw new DeprecationError(
1401
- "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
1656
+ "`wrangler subdomain` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#subdomain for alternatives"
1402
1657
  );
1403
1658
  }
1404
1659
  );
@@ -1409,6 +1664,12 @@ export async function main(argv: string[]): Promise<void> {
1409
1664
  "🤫 Generate a secret that can be referenced in the worker script",
1410
1665
  (secretYargs) => {
1411
1666
  return secretYargs
1667
+ .command(subHelp)
1668
+ .option("legacy-env", {
1669
+ type: "boolean",
1670
+ describe: "Use legacy environments",
1671
+ hidden: true,
1672
+ })
1412
1673
  .command(
1413
1674
  "put <key>",
1414
1675
  "Create or update a secret variable for a script",
@@ -1421,37 +1682,41 @@ export async function main(argv: string[]): Promise<void> {
1421
1682
  .option("name", {
1422
1683
  describe: "Name of the worker",
1423
1684
  type: "string",
1685
+ requiresArg: true,
1424
1686
  })
1425
1687
  .option("env", {
1426
1688
  type: "string",
1689
+ requiresArg: true,
1427
1690
  describe:
1428
1691
  "Binds the secret to the Worker of the specific environment",
1692
+ alias: "e",
1429
1693
  });
1430
1694
  },
1431
1695
  async (args) => {
1432
- const config = readConfig(args.config as ConfigPath);
1696
+ await printWranglerBanner();
1697
+ const config = readConfig(args.config as ConfigPath, args);
1433
1698
 
1434
- const scriptName = getScriptName(args, config);
1699
+ const scriptName = getLegacyScriptName(args, config);
1435
1700
  if (!scriptName) {
1436
1701
  throw new Error("Missing script name");
1437
1702
  }
1438
1703
 
1439
- const isInteractive = process.stdin.isTTY;
1440
- const accountId = await requireAuth(config, isInteractive);
1704
+ const accountId = await requireAuth(config);
1441
1705
 
1706
+ const isInteractive = process.stdin.isTTY;
1442
1707
  const secretValue = isInteractive
1443
1708
  ? await prompt("Enter a secret value:", "password")
1444
1709
  : await readFromStdin();
1445
1710
 
1446
- console.log(
1711
+ logger.log(
1447
1712
  `🌀 Creating the secret for script ${scriptName} ${
1448
- args.env && !isLegacyEnv(args, config) ? `(${args.env})` : ""
1713
+ args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
1449
1714
  }`
1450
1715
  );
1451
1716
 
1452
1717
  async function submitSecret() {
1453
1718
  const url =
1454
- !args.env || isLegacyEnv(args, config)
1719
+ !args.env || isLegacyEnv(config)
1455
1720
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1456
1721
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1457
1722
 
@@ -1469,12 +1734,12 @@ export async function main(argv: string[]): Promise<void> {
1469
1734
  const createDraftWorker = async () => {
1470
1735
  // TODO: log a warning
1471
1736
  await fetchResult(
1472
- !args["legacy-env"] && args.env
1737
+ !isLegacyEnv(config) && args.env
1473
1738
  ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
1474
1739
  : `/accounts/${accountId}/workers/scripts/${scriptName}`,
1475
1740
  {
1476
1741
  method: "PUT",
1477
- body: toFormData({
1742
+ body: createWorkerUploadForm({
1478
1743
  name: scriptName,
1479
1744
  main: {
1480
1745
  name: scriptName,
@@ -1488,6 +1753,7 @@ export async function main(argv: string[]): Promise<void> {
1488
1753
  r2_buckets: [],
1489
1754
  wasm_modules: {},
1490
1755
  text_blobs: {},
1756
+ data_blobs: {},
1491
1757
  unsafe: [],
1492
1758
  },
1493
1759
  modules: [],
@@ -1504,7 +1770,7 @@ export async function main(argv: string[]): Promise<void> {
1504
1770
  return (
1505
1771
  typeof e === "object" &&
1506
1772
  e !== null &&
1507
- (e as { code: 10007 }).code === 10007
1773
+ (e as { code: number }).code === 10007
1508
1774
  );
1509
1775
  }
1510
1776
 
@@ -1521,13 +1787,14 @@ export async function main(argv: string[]): Promise<void> {
1521
1787
  }
1522
1788
  }
1523
1789
 
1524
- console.log(`✨ Success! Uploaded secret ${args.key}`);
1790
+ logger.log(`✨ Success! Uploaded secret ${args.key}`);
1525
1791
  }
1526
1792
  )
1527
1793
  .command(
1528
1794
  "delete <key>",
1529
1795
  "Delete a secret variable from a script",
1530
- (yargs) => {
1796
+ async (yargs) => {
1797
+ await printWranglerBanner();
1531
1798
  return yargs
1532
1799
  .positional("key", {
1533
1800
  describe: "The variable name to be accessible in the script",
@@ -1536,17 +1803,20 @@ export async function main(argv: string[]): Promise<void> {
1536
1803
  .option("name", {
1537
1804
  describe: "Name of the worker",
1538
1805
  type: "string",
1806
+ requiresArg: true,
1539
1807
  })
1540
1808
  .option("env", {
1541
1809
  type: "string",
1810
+ requiresArg: true,
1542
1811
  describe:
1543
1812
  "Binds the secret to the Worker of the specific environment",
1813
+ alias: "e",
1544
1814
  });
1545
1815
  },
1546
1816
  async (args) => {
1547
- const config = readConfig(args.config as ConfigPath);
1817
+ const config = readConfig(args.config as ConfigPath, args);
1548
1818
 
1549
- const scriptName = getScriptName(args, config);
1819
+ const scriptName = getLegacyScriptName(args, config);
1550
1820
  if (!scriptName) {
1551
1821
  throw new Error("Missing script name");
1552
1822
  }
@@ -1558,23 +1828,23 @@ export async function main(argv: string[]): Promise<void> {
1558
1828
  `Are you sure you want to permanently delete the variable ${
1559
1829
  args.key
1560
1830
  } on the script ${scriptName}${
1561
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1831
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1562
1832
  }?`
1563
1833
  )
1564
1834
  ) {
1565
- console.log(
1835
+ logger.log(
1566
1836
  `🌀 Deleting the secret ${args.key} on script ${scriptName}${
1567
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1837
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1568
1838
  }`
1569
1839
  );
1570
1840
 
1571
1841
  const url =
1572
- !args.env || isLegacyEnv(args, config)
1842
+ !args.env || isLegacyEnv(config)
1573
1843
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1574
1844
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1575
1845
 
1576
1846
  await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1577
- console.log(`✨ Success! Deleted secret ${args.key}`);
1847
+ logger.log(`✨ Success! Deleted secret ${args.key}`);
1578
1848
  }
1579
1849
  }
1580
1850
  )
@@ -1586,17 +1856,20 @@ export async function main(argv: string[]): Promise<void> {
1586
1856
  .option("name", {
1587
1857
  describe: "Name of the worker",
1588
1858
  type: "string",
1859
+ requiresArg: true,
1589
1860
  })
1590
1861
  .option("env", {
1591
1862
  type: "string",
1863
+ requiresArg: true,
1592
1864
  describe:
1593
1865
  "Binds the secret to the Worker of the specific environment.",
1866
+ alias: "e",
1594
1867
  });
1595
1868
  },
1596
1869
  async (args) => {
1597
- const config = readConfig(args.config as ConfigPath);
1870
+ const config = readConfig(args.config as ConfigPath, args);
1598
1871
 
1599
- const scriptName = getScriptName(args, config);
1872
+ const scriptName = getLegacyScriptName(args, config);
1600
1873
  if (!scriptName) {
1601
1874
  throw new Error("Missing script name");
1602
1875
  }
@@ -1604,11 +1877,11 @@ export async function main(argv: string[]): Promise<void> {
1604
1877
  const accountId = await requireAuth(config);
1605
1878
 
1606
1879
  const url =
1607
- !args.env || isLegacyEnv(args, config)
1880
+ !args.env || isLegacyEnv(config)
1608
1881
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1609
1882
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1610
1883
 
1611
- console.log(JSON.stringify(await fetchResult(url), null, " "));
1884
+ logger.log(JSON.stringify(await fetchResult(url), null, " "));
1612
1885
  }
1613
1886
  );
1614
1887
  }
@@ -1621,6 +1894,7 @@ export async function main(argv: string[]): Promise<void> {
1621
1894
  "🗂️ Interact with your Workers KV Namespaces",
1622
1895
  (namespaceYargs) => {
1623
1896
  return namespaceYargs
1897
+ .command(subHelp)
1624
1898
  .command(
1625
1899
  "create <namespace>",
1626
1900
  "Create a new namespace",
@@ -1633,7 +1907,9 @@ export async function main(argv: string[]): Promise<void> {
1633
1907
  })
1634
1908
  .option("env", {
1635
1909
  type: "string",
1910
+ requiresArg: true,
1636
1911
  describe: "Perform on a specific environment",
1912
+ alias: "e",
1637
1913
  })
1638
1914
  .option("preview", {
1639
1915
  type: "boolean",
@@ -1641,12 +1917,7 @@ export async function main(argv: string[]): Promise<void> {
1641
1917
  });
1642
1918
  },
1643
1919
  async (args) => {
1644
- if (args._.length > 2) {
1645
- const extraArgs = args._.slice(2).join(" ");
1646
- throw new CommandLineArgsError(
1647
- `Unexpected additional positional arguments "${extraArgs}".`
1648
- );
1649
- }
1920
+ await printWranglerBanner();
1650
1921
 
1651
1922
  if (!isValidNamespaceBinding(args.namespace)) {
1652
1923
  throw new CommandLineArgsError(
@@ -1654,9 +1925,9 @@ export async function main(argv: string[]): Promise<void> {
1654
1925
  );
1655
1926
  }
1656
1927
 
1657
- const config = readConfig(args.config as ConfigPath);
1928
+ const config = readConfig(args.config as ConfigPath, args);
1658
1929
  if (!config.name) {
1659
- console.warn(
1930
+ logger.warn(
1660
1931
  "No configured name present, using `worker` as a prefix for the title"
1661
1932
  );
1662
1933
  }
@@ -1670,16 +1941,16 @@ export async function main(argv: string[]): Promise<void> {
1670
1941
 
1671
1942
  // TODO: generate a binding name stripping non alphanumeric chars
1672
1943
 
1673
- console.log(`🌀 Creating namespace with title "${title}"`);
1944
+ logger.log(`🌀 Creating namespace with title "${title}"`);
1674
1945
  const namespaceId = await createNamespace(accountId, title);
1675
1946
 
1676
- console.log("✨ Success!");
1947
+ logger.log("✨ Success!");
1677
1948
  const envString = args.env ? ` under [env.${args.env}]` : "";
1678
1949
  const previewString = args.preview ? "preview_" : "";
1679
- console.log(
1950
+ logger.log(
1680
1951
  `Add the following to your configuration file in your kv_namespaces array${envString}:`
1681
1952
  );
1682
- console.log(
1953
+ logger.log(
1683
1954
  `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1684
1955
  );
1685
1956
 
@@ -1691,13 +1962,13 @@ export async function main(argv: string[]): Promise<void> {
1691
1962
  "Outputs a list of all KV namespaces associated with your account id.",
1692
1963
  {},
1693
1964
  async (args) => {
1694
- const config = readConfig(args.config as ConfigPath);
1965
+ const config = readConfig(args.config as ConfigPath, args);
1695
1966
 
1696
1967
  const accountId = await requireAuth(config);
1697
1968
 
1698
1969
  // TODO: we should show bindings if they exist for given ids
1699
1970
 
1700
- console.log(
1971
+ logger.log(
1701
1972
  JSON.stringify(await listNamespaces(accountId), null, " ")
1702
1973
  );
1703
1974
  }
@@ -1709,16 +1980,20 @@ export async function main(argv: string[]): Promise<void> {
1709
1980
  return yargs
1710
1981
  .option("binding", {
1711
1982
  type: "string",
1983
+ requiresArg: true,
1712
1984
  describe: "The name of the namespace to delete",
1713
1985
  })
1714
1986
  .option("namespace-id", {
1715
1987
  type: "string",
1988
+ requiresArg: true,
1716
1989
  describe: "The id of the namespace to delete",
1717
1990
  })
1718
1991
  .check(demandOneOfOption("binding", "namespace-id"))
1719
1992
  .option("env", {
1720
1993
  type: "string",
1994
+ requiresArg: true,
1721
1995
  describe: "Perform on a specific environment",
1996
+ alias: "e",
1722
1997
  })
1723
1998
  .option("preview", {
1724
1999
  type: "boolean",
@@ -1726,7 +2001,8 @@ export async function main(argv: string[]): Promise<void> {
1726
2001
  });
1727
2002
  },
1728
2003
  async (args) => {
1729
- const config = readConfig(args.config as ConfigPath);
2004
+ await printWranglerBanner();
2005
+ const config = readConfig(args.config as ConfigPath, args);
1730
2006
 
1731
2007
  let id;
1732
2008
  try {
@@ -1772,6 +2048,7 @@ export async function main(argv: string[]): Promise<void> {
1772
2048
  "🔑 Individually manage Workers KV key-value pairs",
1773
2049
  (kvKeyYargs) => {
1774
2050
  return kvKeyYargs
2051
+ .command(subHelp)
1775
2052
  .command(
1776
2053
  "put <key> [value]",
1777
2054
  "Writes a single key/value pair to the given namespace.",
@@ -1788,16 +2065,20 @@ export async function main(argv: string[]): Promise<void> {
1788
2065
  })
1789
2066
  .option("binding", {
1790
2067
  type: "string",
2068
+ requiresArg: true,
1791
2069
  describe: "The binding of the namespace to write to",
1792
2070
  })
1793
2071
  .option("namespace-id", {
1794
2072
  type: "string",
2073
+ requiresArg: true,
1795
2074
  describe: "The id of the namespace to write to",
1796
2075
  })
1797
2076
  .check(demandOneOfOption("binding", "namespace-id"))
1798
2077
  .option("env", {
1799
2078
  type: "string",
2079
+ requiresArg: true,
1800
2080
  describe: "Perform on a specific environment",
2081
+ alias: "e",
1801
2082
  })
1802
2083
  .option("preview", {
1803
2084
  type: "boolean",
@@ -1814,12 +2095,14 @@ export async function main(argv: string[]): Promise<void> {
1814
2095
  })
1815
2096
  .option("path", {
1816
2097
  type: "string",
2098
+ requiresArg: true,
1817
2099
  describe: "Read value from the file at a given path",
1818
2100
  })
1819
2101
  .check(demandOneOfOption("value", "path"));
1820
2102
  },
1821
2103
  async ({ key, ttl, expiration, ...args }) => {
1822
- const config = readConfig(args.config as ConfigPath);
2104
+ await printWranglerBanner();
2105
+ const config = readConfig(args.config as ConfigPath, args);
1823
2106
  const namespaceId = getNamespaceId(args, config);
1824
2107
  // One of `args.path` and `args.value` must be defined
1825
2108
  const value = args.path
@@ -1828,12 +2111,12 @@ export async function main(argv: string[]): Promise<void> {
1828
2111
  args.value!;
1829
2112
 
1830
2113
  if (args.path) {
1831
- console.log(
1832
- `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}`
2114
+ logger.log(
2115
+ `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.`
1833
2116
  );
1834
2117
  } else {
1835
- console.log(
1836
- `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
2118
+ logger.log(
2119
+ `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
1837
2120
  );
1838
2121
  }
1839
2122
 
@@ -1854,16 +2137,20 @@ export async function main(argv: string[]): Promise<void> {
1854
2137
  return yargs
1855
2138
  .option("binding", {
1856
2139
  type: "string",
2140
+ requiresArg: true,
1857
2141
  describe: "The name of the namespace to list",
1858
2142
  })
1859
2143
  .option("namespace-id", {
1860
2144
  type: "string",
2145
+ requiresArg: true,
1861
2146
  describe: "The id of the namespace to list",
1862
2147
  })
1863
2148
  .check(demandOneOfOption("binding", "namespace-id"))
1864
2149
  .option("env", {
1865
2150
  type: "string",
2151
+ requiresArg: true,
1866
2152
  describe: "Perform on a specific environment",
2153
+ alias: "e",
1867
2154
  })
1868
2155
  .option("preview", {
1869
2156
  type: "boolean",
@@ -1873,12 +2160,13 @@ export async function main(argv: string[]): Promise<void> {
1873
2160
  })
1874
2161
  .option("prefix", {
1875
2162
  type: "string",
2163
+ requiresArg: true,
1876
2164
  describe: "A prefix to filter listed keys",
1877
2165
  });
1878
2166
  },
1879
2167
  async ({ prefix, ...args }) => {
1880
2168
  // TODO: support for limit+cursor (pagination)
1881
- const config = readConfig(args.config as ConfigPath);
2169
+ const config = readConfig(args.config as ConfigPath, args);
1882
2170
  const namespaceId = getNamespaceId(args, config);
1883
2171
 
1884
2172
  const accountId = await requireAuth(config);
@@ -1888,7 +2176,7 @@ export async function main(argv: string[]): Promise<void> {
1888
2176
  namespaceId,
1889
2177
  prefix
1890
2178
  );
1891
- console.log(JSON.stringify(results, undefined, 2));
2179
+ logger.log(JSON.stringify(results, undefined, 2));
1892
2180
  }
1893
2181
  )
1894
2182
  .command(
@@ -1903,16 +2191,20 @@ export async function main(argv: string[]): Promise<void> {
1903
2191
  })
1904
2192
  .option("binding", {
1905
2193
  type: "string",
2194
+ requiresArg: true,
1906
2195
  describe: "The name of the namespace to get from",
1907
2196
  })
1908
2197
  .option("namespace-id", {
1909
2198
  type: "string",
2199
+ requiresArg: true,
1910
2200
  describe: "The id of the namespace to get from",
1911
2201
  })
1912
2202
  .check(demandOneOfOption("binding", "namespace-id"))
1913
2203
  .option("env", {
1914
2204
  type: "string",
2205
+ requiresArg: true,
1915
2206
  describe: "Perform on a specific environment",
2207
+ alias: "e",
1916
2208
  })
1917
2209
  .option("preview", {
1918
2210
  type: "boolean",
@@ -1926,12 +2218,12 @@ export async function main(argv: string[]): Promise<void> {
1926
2218
  });
1927
2219
  },
1928
2220
  async ({ key, ...args }) => {
1929
- const config = readConfig(args.config as ConfigPath);
2221
+ const config = readConfig(args.config as ConfigPath, args);
1930
2222
  const namespaceId = getNamespaceId(args, config);
1931
2223
 
1932
2224
  const accountId = await requireAuth(config);
1933
2225
 
1934
- console.log(await getKeyValue(accountId, namespaceId, key));
2226
+ logger.log(await getKeyValue(accountId, namespaceId, key));
1935
2227
  }
1936
2228
  )
1937
2229
  .command(
@@ -1946,16 +2238,20 @@ export async function main(argv: string[]): Promise<void> {
1946
2238
  })
1947
2239
  .option("binding", {
1948
2240
  type: "string",
2241
+ requiresArg: true,
1949
2242
  describe: "The name of the namespace to delete from",
1950
2243
  })
1951
2244
  .option("namespace-id", {
1952
2245
  type: "string",
2246
+ requiresArg: true,
1953
2247
  describe: "The id of the namespace to delete from",
1954
2248
  })
1955
2249
  .check(demandOneOfOption("binding", "namespace-id"))
1956
2250
  .option("env", {
1957
2251
  type: "string",
2252
+ requiresArg: true,
1958
2253
  describe: "Perform on a specific environment",
2254
+ alias: "e",
1959
2255
  })
1960
2256
  .option("preview", {
1961
2257
  type: "boolean",
@@ -1963,11 +2259,12 @@ export async function main(argv: string[]): Promise<void> {
1963
2259
  });
1964
2260
  },
1965
2261
  async ({ key, ...args }) => {
1966
- const config = readConfig(args.config as ConfigPath);
2262
+ await printWranglerBanner();
2263
+ const config = readConfig(args.config as ConfigPath, args);
1967
2264
  const namespaceId = getNamespaceId(args, config);
1968
2265
 
1969
- console.log(
1970
- `deleting the key "${key}" on namespace ${namespaceId}`
2266
+ logger.log(
2267
+ `Deleting the key "${key}" on namespace ${namespaceId}.`
1971
2268
  );
1972
2269
 
1973
2270
  const accountId = await requireAuth(config);
@@ -1987,6 +2284,7 @@ export async function main(argv: string[]): Promise<void> {
1987
2284
  "💪 Interact with multiple Workers KV key-value pairs at once",
1988
2285
  (kvBulkYargs) => {
1989
2286
  return kvBulkYargs
2287
+ .command(subHelp)
1990
2288
  .command(
1991
2289
  "put <filename>",
1992
2290
  "Upload multiple key-value pairs to a namespace",
@@ -1999,16 +2297,20 @@ export async function main(argv: string[]): Promise<void> {
1999
2297
  })
2000
2298
  .option("binding", {
2001
2299
  type: "string",
2300
+ requiresArg: true,
2002
2301
  describe: "The name of the namespace to insert values into",
2003
2302
  })
2004
2303
  .option("namespace-id", {
2005
2304
  type: "string",
2305
+ requiresArg: true,
2006
2306
  describe: "The id of the namespace to insert values into",
2007
2307
  })
2008
2308
  .check(demandOneOfOption("binding", "namespace-id"))
2009
2309
  .option("env", {
2010
2310
  type: "string",
2311
+ requiresArg: true,
2011
2312
  describe: "Perform on a specific environment",
2313
+ alias: "e",
2012
2314
  })
2013
2315
  .option("preview", {
2014
2316
  type: "boolean",
@@ -2016,11 +2318,12 @@ export async function main(argv: string[]): Promise<void> {
2016
2318
  });
2017
2319
  },
2018
2320
  async ({ filename, ...args }) => {
2321
+ await printWranglerBanner();
2019
2322
  // The simplest implementation I could think of.
2020
2323
  // This could be made more efficient with a streaming parser/uploader
2021
2324
  // but we'll do that in the future if needed.
2022
2325
 
2023
- const config = readConfig(args.config as ConfigPath);
2326
+ const config = readConfig(args.config as ConfigPath, args);
2024
2327
  const namespaceId = getNamespaceId(args, config);
2025
2328
  const content = parseJSON(readFileSync(filename), filename);
2026
2329
 
@@ -2057,7 +2360,7 @@ export async function main(argv: string[]): Promise<void> {
2057
2360
  }
2058
2361
  }
2059
2362
  if (warnings.length > 0) {
2060
- console.warn(
2363
+ logger.warn(
2061
2364
  `Unexpected key-value properties in "${filename}".\n` +
2062
2365
  warnings.join("\n")
2063
2366
  );
@@ -2084,11 +2387,11 @@ export async function main(argv: string[]): Promise<void> {
2084
2387
  namespaceId,
2085
2388
  content,
2086
2389
  (index, total) => {
2087
- console.log(`Uploaded ${index} of ${total}.`);
2390
+ logger.log(`Uploaded ${index} of ${total}.`);
2088
2391
  }
2089
2392
  );
2090
2393
 
2091
- console.log("Success!");
2394
+ logger.log("Success!");
2092
2395
  }
2093
2396
  )
2094
2397
  .command(
@@ -2103,16 +2406,20 @@ export async function main(argv: string[]): Promise<void> {
2103
2406
  })
2104
2407
  .option("binding", {
2105
2408
  type: "string",
2409
+ requiresArg: true,
2106
2410
  describe: "The name of the namespace to delete from",
2107
2411
  })
2108
2412
  .option("namespace-id", {
2109
2413
  type: "string",
2414
+ requiresArg: true,
2110
2415
  describe: "The id of the namespace to delete from",
2111
2416
  })
2112
2417
  .check(demandOneOfOption("binding", "namespace-id"))
2113
2418
  .option("env", {
2114
2419
  type: "string",
2420
+ requiresArg: true,
2115
2421
  describe: "Perform on a specific environment",
2422
+ alias: "e",
2116
2423
  })
2117
2424
  .option("preview", {
2118
2425
  type: "boolean",
@@ -2125,7 +2432,8 @@ export async function main(argv: string[]): Promise<void> {
2125
2432
  });
2126
2433
  },
2127
2434
  async ({ filename, ...args }) => {
2128
- const config = readConfig(args.config as ConfigPath);
2435
+ await printWranglerBanner();
2436
+ const config = readConfig(args.config as ConfigPath, args);
2129
2437
  const namespaceId = getNamespaceId(args, config);
2130
2438
 
2131
2439
  if (!args.force) {
@@ -2133,7 +2441,7 @@ export async function main(argv: string[]): Promise<void> {
2133
2441
  `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
2134
2442
  );
2135
2443
  if (!result) {
2136
- console.log(`Not deleting keys read from "${filename}".`);
2444
+ logger.log(`Not deleting keys read from "${filename}".`);
2137
2445
  return;
2138
2446
  }
2139
2447
  }
@@ -2176,123 +2484,189 @@ export async function main(argv: string[]): Promise<void> {
2176
2484
  namespaceId,
2177
2485
  content,
2178
2486
  (index, total) => {
2179
- console.log(`Deleted ${index} of ${total}.`);
2487
+ logger.log(`Deleted ${index} of ${total}.`);
2180
2488
  }
2181
2489
  );
2182
2490
 
2183
- console.log("Success!");
2491
+ logger.log("Success!");
2184
2492
  }
2185
2493
  );
2186
2494
  }
2187
2495
  );
2188
2496
 
2189
- wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
2497
+ wrangler.command(
2498
+ "pages",
2499
+ "⚡️ Configure Cloudflare Pages",
2500
+ async (pagesYargs) => {
2501
+ await pages(pagesYargs.command(subHelp).epilogue(pagesBetaWarning));
2502
+ }
2503
+ );
2190
2504
 
2191
2505
  wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
2192
- return r2Yargs.command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
2193
- r2BucketYargs.command(
2194
- "create <name>",
2195
- "Create a new R2 bucket",
2196
- (yargs) => {
2197
- return yargs.positional("name", {
2198
- describe: "The name of the new bucket",
2199
- type: "string",
2200
- demandOption: true,
2201
- });
2202
- },
2203
- async (args) => {
2204
- // We expect three values in `_`: `r2`, `bucket`, `create`.
2205
- if (args._.length > 3) {
2206
- const extraArgs = args._.slice(3).join(" ");
2207
- throw new CommandLineArgsError(
2208
- `Unexpected additional positional arguments "${extraArgs}".`
2209
- );
2506
+ return r2Yargs
2507
+ .command(subHelp)
2508
+ .command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
2509
+ r2BucketYargs.command(
2510
+ "create <name>",
2511
+ "Create a new R2 bucket",
2512
+ (yargs) => {
2513
+ return yargs.positional("name", {
2514
+ describe: "The name of the new bucket",
2515
+ type: "string",
2516
+ demandOption: true,
2517
+ });
2518
+ },
2519
+ async (args) => {
2520
+ await printWranglerBanner();
2521
+
2522
+ const config = readConfig(args.config as ConfigPath, args);
2523
+
2524
+ const accountId = await requireAuth(config);
2525
+
2526
+ logger.log(`Creating bucket ${args.name}.`);
2527
+ await createR2Bucket(accountId, args.name);
2528
+ logger.log(`Created bucket ${args.name}.`);
2210
2529
  }
2530
+ );
2211
2531
 
2212
- const config = readConfig(args.config as ConfigPath);
2532
+ r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
2533
+ const config = readConfig(args.config as ConfigPath, args);
2213
2534
 
2214
2535
  const accountId = await requireAuth(config);
2215
2536
 
2216
- console.log(`Creating bucket ${args.name}.`);
2217
- await createR2Bucket(accountId, args.name);
2218
- console.log(`Created bucket ${args.name}.`);
2219
- }
2220
- );
2537
+ logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2538
+ });
2221
2539
 
2222
- r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
2223
- const config = readConfig(args.config as ConfigPath);
2540
+ r2BucketYargs.command(
2541
+ "delete <name>",
2542
+ "Delete an R2 bucket",
2543
+ (yargs) => {
2544
+ return yargs.positional("name", {
2545
+ describe: "The name of the bucket to delete",
2546
+ type: "string",
2547
+ demandOption: true,
2548
+ });
2549
+ },
2550
+ async (args) => {
2551
+ await printWranglerBanner();
2224
2552
 
2225
- const accountId = await requireAuth(config);
2553
+ const config = readConfig(args.config as ConfigPath, args);
2226
2554
 
2227
- console.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2228
- });
2555
+ const accountId = await requireAuth(config);
2229
2556
 
2230
- r2BucketYargs.command(
2231
- "delete <name>",
2232
- "Delete an R2 bucket",
2233
- (yargs) => {
2234
- return yargs.positional("name", {
2235
- describe: "The name of the bucket to delete",
2236
- type: "string",
2237
- demandOption: true,
2238
- });
2239
- },
2240
- async (args) => {
2241
- // We expect three values in `_`: `r2`, `bucket`, `delete`.
2242
- if (args._.length > 3) {
2243
- const extraArgs = args._.slice(3).join(" ");
2244
- throw new CommandLineArgsError(
2245
- `Unexpected additional positional arguments "${extraArgs}".`
2246
- );
2557
+ logger.log(`Deleting bucket ${args.name}.`);
2558
+ await deleteR2Bucket(accountId, args.name);
2559
+ logger.log(`Deleted bucket ${args.name}.`);
2247
2560
  }
2561
+ );
2562
+ return r2BucketYargs;
2563
+ });
2564
+ });
2248
2565
 
2249
- const config = readConfig(args.config as ConfigPath);
2250
-
2251
- const accountId = await requireAuth(config);
2566
+ /**
2567
+ * User Group: login, logout, and whoami
2568
+ * TODO: group commands into User group similar to .group() for flags in yargs
2569
+ */
2570
+ // login
2571
+ wrangler.command(
2572
+ // this needs scopes as an option?
2573
+ "login",
2574
+ "🔓 Login to Cloudflare",
2575
+ (yargs) => {
2576
+ // TODO: This needs some copy editing
2577
+ // I mean, this entire app does, but this too.
2578
+ return yargs
2579
+ .option("scopes-list", {
2580
+ describe: "List all the available OAuth scopes with descriptions",
2581
+ })
2582
+ .option("scopes", {
2583
+ describe: "Pick the set of applicable OAuth scopes when logging in",
2584
+ array: true,
2585
+ type: "string",
2586
+ requiresArg: true,
2587
+ });
2252
2588
 
2253
- console.log(`Deleting bucket ${args.name}.`);
2254
- await deleteR2Bucket(accountId, args.name);
2255
- console.log(`Deleted bucket ${args.name}.`);
2589
+ // TODO: scopes
2590
+ },
2591
+ async (args) => {
2592
+ await printWranglerBanner();
2593
+ if (args["scopes-list"]) {
2594
+ listScopes();
2595
+ return;
2596
+ }
2597
+ if (args.scopes) {
2598
+ if (args.scopes.length === 0) {
2599
+ // don't allow no scopes to be passed, that would be weird
2600
+ listScopes();
2601
+ return;
2256
2602
  }
2257
- );
2258
- return r2BucketYargs;
2259
- });
2260
- });
2603
+ if (!validateScopeKeys(args.scopes)) {
2604
+ throw new CommandLineArgsError(
2605
+ `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
2606
+ );
2607
+ }
2608
+ await login({ scopes: args.scopes });
2609
+ return;
2610
+ }
2611
+ await login();
2261
2612
 
2262
- wrangler
2263
- .option("legacy-env", {
2264
- type: "boolean",
2265
- describe: "Use legacy environments",
2266
- })
2267
- .option("config", {
2268
- alias: "c",
2269
- describe: "Path to .toml configuration file",
2270
- type: "string",
2271
- });
2613
+ // TODO: would be nice if it optionally saved login
2614
+ // credentials inside node_modules/.cache or something
2615
+ // this way you could have multiple users on a single machine
2616
+ }
2617
+ );
2272
2618
 
2273
- wrangler.group(["config", "help", "version", "legacy-env"], "Flags:");
2619
+ // logout
2620
+ wrangler.command(
2621
+ // this needs scopes as an option?
2622
+ "logout",
2623
+ "🚪 Logout from Cloudflare",
2624
+ () => {},
2625
+ async () => {
2626
+ await printWranglerBanner();
2627
+ await logout();
2628
+ }
2629
+ );
2630
+
2631
+ // whoami
2632
+ wrangler.command(
2633
+ "whoami",
2634
+ "🕵️ Retrieve your user info and test your auth config",
2635
+ () => {},
2636
+ async () => {
2637
+ await printWranglerBanner();
2638
+ await whoami();
2639
+ }
2640
+ );
2641
+
2642
+ wrangler.option("config", {
2643
+ alias: "c",
2644
+ describe: "Path to .toml configuration file",
2645
+ type: "string",
2646
+ requiresArg: true,
2647
+ });
2648
+
2649
+ wrangler.group(["config", "help", "version"], "Flags:");
2274
2650
  wrangler.help().alias("h", "help");
2275
2651
  wrangler.version(wranglerVersion).alias("v", "version");
2276
2652
  wrangler.exitProcess(false);
2277
2653
 
2278
2654
  try {
2279
- await initialiseUserConfig();
2280
2655
  await wrangler.parse();
2281
2656
  } catch (e) {
2657
+ logger.log(""); // Just adds a bit of space
2282
2658
  if (e instanceof CommandLineArgsError) {
2283
2659
  wrangler.showHelp("error");
2284
- console.error(""); // Just adds a bit of space
2285
- console.error(e.message);
2660
+ logger.log(""); // Add a bit of space.
2661
+ logger.error(e.message);
2286
2662
  } else if (e instanceof ParseError) {
2287
- console.error("");
2288
2663
  e.notes.push({
2289
2664
  text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new",
2290
2665
  });
2291
- console.error(formatMessage(e));
2666
+ logger.error(formatMessage(e));
2292
2667
  } else {
2293
- console.error(e instanceof Error ? e.message : e);
2294
- console.error(""); // Just adds a bit of space
2295
- console.error(
2668
+ logger.error(e instanceof Error ? e.message : e);
2669
+ logger.log(
2296
2670
  `${fgGreenColor}%s${resetColor}`,
2297
2671
  "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2298
2672
  );
@@ -2300,3 +2674,25 @@ export async function main(argv: string[]): Promise<void> {
2300
2674
  throw e;
2301
2675
  }
2302
2676
  }
2677
+
2678
+ function getDevCompatibilityDate(
2679
+ config: Config,
2680
+ compatibilityDate = config.compatibility_date
2681
+ ) {
2682
+ const currentDate = new Date().toISOString().substring(0, 10);
2683
+ if (config.configPath !== undefined && compatibilityDate === undefined) {
2684
+ logger.warn(
2685
+ `No compatibility_date was specified. Using today's date: ${currentDate}.\n` +
2686
+ "Add one to your wrangler.toml file:\n" +
2687
+ "```\n" +
2688
+ `compatibility_date = "${currentDate}"\n` +
2689
+ "```\n" +
2690
+ "or pass it in your terminal:\n" +
2691
+ "```\n" +
2692
+ `--compatibility-date=${currentDate}\n` +
2693
+ "```\n" +
2694
+ "See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
2695
+ );
2696
+ }
2697
+ return compatibilityDate ?? currentDate;
2698
+ }