wrangler 2.0.22 → 2.0.25

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 (75) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +643 -7
  4. package/package.json +17 -5
  5. package/src/__tests__/configuration.test.ts +89 -17
  6. package/src/__tests__/dev.test.tsx +121 -8
  7. package/src/__tests__/generate.test.ts +93 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +54 -2
  9. package/src/__tests__/index.test.ts +10 -27
  10. package/src/__tests__/jest.setup.ts +31 -1
  11. package/src/__tests__/kv.test.ts +82 -61
  12. package/src/__tests__/metrics.test.ts +5 -0
  13. package/src/__tests__/publish.test.ts +573 -254
  14. package/src/__tests__/r2.test.ts +173 -71
  15. package/src/__tests__/tail.test.ts +93 -39
  16. package/src/__tests__/user.test.ts +1 -0
  17. package/src/__tests__/validate-dev-props.test.ts +56 -0
  18. package/src/__tests__/version.test.ts +35 -0
  19. package/src/__tests__/whoami.test.tsx +60 -1
  20. package/src/api/dev.ts +49 -9
  21. package/src/bundle.ts +298 -37
  22. package/src/cfetch/internal.ts +34 -2
  23. package/src/config/config.ts +15 -3
  24. package/src/config/environment.ts +40 -8
  25. package/src/config/index.ts +13 -0
  26. package/src/config/validation.ts +111 -9
  27. package/src/create-worker-preview.ts +3 -1
  28. package/src/create-worker-upload-form.ts +25 -0
  29. package/src/dev/dev.tsx +145 -31
  30. package/src/dev/local.tsx +116 -24
  31. package/src/dev/remote.tsx +39 -12
  32. package/src/dev/use-esbuild.ts +28 -0
  33. package/src/dev/validate-dev-props.ts +31 -0
  34. package/src/dev-registry.tsx +160 -0
  35. package/src/dev.tsx +148 -67
  36. package/src/generate.ts +112 -14
  37. package/src/index.tsx +252 -7
  38. package/src/inspect.ts +90 -5
  39. package/src/metrics/index.ts +1 -0
  40. package/src/metrics/metrics-dispatcher.ts +1 -0
  41. package/src/metrics/metrics-usage-headers.ts +24 -0
  42. package/src/metrics/send-event.ts +2 -2
  43. package/src/miniflare-cli/assets.ts +546 -0
  44. package/src/miniflare-cli/index.ts +157 -6
  45. package/src/module-collection.ts +3 -3
  46. package/src/pages/build.tsx +36 -28
  47. package/src/pages/constants.ts +4 -0
  48. package/src/pages/deployments.tsx +10 -10
  49. package/src/pages/dev.tsx +155 -651
  50. package/src/pages/functions/buildPlugin.ts +4 -0
  51. package/src/pages/functions/buildWorker.ts +4 -0
  52. package/src/pages/functions/routes-consolidation.test.ts +66 -0
  53. package/src/pages/functions/routes-consolidation.ts +29 -0
  54. package/src/pages/functions/routes-transformation.test.ts +271 -0
  55. package/src/pages/functions/routes-transformation.ts +125 -0
  56. package/src/pages/projects.tsx +9 -3
  57. package/src/pages/publish.tsx +57 -15
  58. package/src/pages/types.ts +9 -0
  59. package/src/pages/upload.tsx +38 -21
  60. package/src/publish.ts +139 -112
  61. package/src/r2.ts +81 -0
  62. package/src/tail/index.ts +15 -2
  63. package/src/tail/printing.ts +41 -3
  64. package/src/user/choose-account.tsx +20 -11
  65. package/src/user/user.tsx +20 -2
  66. package/src/whoami.tsx +79 -1
  67. package/src/worker.ts +12 -0
  68. package/templates/first-party-worker-module-facade.ts +18 -0
  69. package/templates/format-dev-errors.ts +32 -0
  70. package/templates/pages-shim.ts +9 -0
  71. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  72. package/templates/service-bindings-module-facade.js +51 -0
  73. package/templates/service-bindings-sw-facade.js +39 -0
  74. package/wrangler-dist/cli.d.ts +38 -3
  75. package/wrangler-dist/cli.js +45244 -25199
package/src/dev.tsx CHANGED
@@ -22,10 +22,12 @@ import {
22
22
  getDevCompatibilityDate,
23
23
  getRules,
24
24
  isLegacyEnv,
25
+ DEFAULT_INSPECTOR_PORT,
25
26
  } from "./index";
26
27
 
27
28
  import type { Config } from "./config";
28
29
  import type { Route } from "./config/environment";
30
+ import type { EnablePagesAssetsServiceBindingOptions } from "./miniflare-cli";
29
31
  import type { CfWorkerInit } from "./worker";
30
32
  import type { RequestInit } from "undici";
31
33
  import type { Argv, ArgumentsCamelCase } from "yargs";
@@ -63,8 +65,10 @@ interface DevArgs {
63
65
  minify?: boolean;
64
66
  "node-compat"?: boolean;
65
67
  "experimental-enable-local-persistence"?: boolean;
68
+ "live-reload"?: boolean;
66
69
  onReady?: () => void;
67
70
  logLevel?: "none" | "error" | "log" | "warn" | "debug";
71
+ logPrefix?: string;
68
72
  showInteractiveDevSession?: boolean;
69
73
  }
70
74
 
@@ -123,9 +127,9 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
123
127
  default: true,
124
128
  })
125
129
  .option("ip", {
126
- describe: "IP address to listen on, defaults to `localhost`",
130
+ describe: "IP address to listen on",
127
131
  type: "string",
128
- requiresArg: true,
132
+ default: "0.0.0.0",
129
133
  })
130
134
  .option("port", {
131
135
  describe: "Port to listen on",
@@ -225,6 +229,12 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
225
229
  describe: "Enable persistence for this session (only for local mode)",
226
230
  type: "boolean",
227
231
  })
232
+ .option("live-reload", {
233
+ // TODO: Add back in once we have remote `--live-reload`
234
+ hidden: true,
235
+ // describe: "Auto reload HTML pages when change is detected",
236
+ type: "boolean",
237
+ })
228
238
  .option("inspect", {
229
239
  describe: "Enable dev tools",
230
240
  type: "boolean",
@@ -250,7 +260,36 @@ export async function devHandler(args: ArgumentsCamelCase<DevArgs>) {
250
260
  }
251
261
  }
252
262
 
253
- export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
263
+ export type AdditionalDevProps = {
264
+ vars?: {
265
+ [key: string]: unknown;
266
+ };
267
+ kv?: {
268
+ binding: string;
269
+ id: string;
270
+ preview_id?: string;
271
+ }[];
272
+ durableObjects?: {
273
+ name: string;
274
+ class_name: string;
275
+ script_name?: string | undefined;
276
+ environment?: string | undefined;
277
+ }[];
278
+ r2?: {
279
+ binding: string;
280
+ bucket_name: string;
281
+ preview_bucket_name?: string;
282
+ }[];
283
+ };
284
+ type StartDevOptions = ArgumentsCamelCase<DevArgs> &
285
+ // These options can be passed in directly when called with the `wrangler.dev()` API.
286
+ // They aren't exposed as CLI arguments.
287
+ AdditionalDevProps & {
288
+ forceLocal?: boolean;
289
+ enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
290
+ };
291
+
292
+ export async function startDev(args: StartDevOptions) {
254
293
  let watcher: ReturnType<typeof watch> | undefined;
255
294
  let rerender: (node: React.ReactNode) => void | undefined;
256
295
  try {
@@ -265,11 +304,6 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
265
304
  ((args.script &&
266
305
  findWranglerToml(path.dirname(args.script))) as ConfigPath);
267
306
  let config = readConfig(configPath, args);
268
- await metrics.sendMetricsEvent(
269
- "run dev",
270
- { local: args.local },
271
- { sendMetrics: config.send_metrics, offline: args.local }
272
- );
273
307
 
274
308
  if (config.configPath) {
275
309
  watcher = watch(config.configPath, {
@@ -291,6 +325,15 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
291
325
  "dev"
292
326
  );
293
327
 
328
+ await metrics.sendMetricsEvent(
329
+ "run dev",
330
+ {
331
+ local: args.local,
332
+ usesTypeScript: /\.tsx?$/.test(entry.file),
333
+ },
334
+ { sendMetrics: config.send_metrics, offline: args.local }
335
+ );
336
+
294
337
  if (config.services && config.services.length > 0) {
295
338
  logger.warn(
296
339
  `This worker is bound to live services: ${config.services
@@ -351,6 +394,10 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
351
394
  const routes: Route[] | undefined =
352
395
  args.routes || (config.route && [config.route]) || config.routes;
353
396
 
397
+ if (args.forceLocal) {
398
+ args.local = true;
399
+ }
400
+
354
401
  if (!args.local) {
355
402
  if (host) {
356
403
  zoneId = await getZoneIdFromHost(host);
@@ -370,73 +417,26 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
370
417
  }
371
418
  }
372
419
 
373
- const nodeCompat = args["node-compat"] ?? config.node_compat;
420
+ const nodeCompat = args.nodeCompat ?? config.node_compat;
374
421
  if (nodeCompat) {
375
422
  logger.warn(
376
423
  "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."
377
424
  );
378
425
  }
379
426
 
380
- // eslint-disable-next-line no-inner-declarations
381
- async function getBindings(
382
- configParam: Config
383
- ): Promise<CfWorkerInit["bindings"]> {
384
- return {
385
- kv_namespaces: configParam.kv_namespaces?.map(
386
- ({ binding, preview_id, id: _id }) => {
387
- // In `dev`, we make folks use a separate kv namespace called
388
- // `preview_id` instead of `id` so that they don't
389
- // break production data. So here we check that a `preview_id`
390
- // has actually been configured.
391
- // This whole block of code will be obsoleted in the future
392
- // when we have copy-on-write for previews on edge workers.
393
- if (!preview_id) {
394
- // TODO: This error has to be a _lot_ better, ideally just asking
395
- // to create a preview namespace for the user automatically
396
- throw new Error(
397
- `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
398
- ); // Ugh, I really don't like this message very much
399
- }
400
- return {
401
- binding,
402
- id: preview_id,
403
- };
404
- }
405
- ),
406
- // Use a copy of combinedVars since we're modifying it later
407
- vars: getVarsForDev(configParam),
408
- wasm_modules: configParam.wasm_modules,
409
- text_blobs: configParam.text_blobs,
410
- data_blobs: configParam.data_blobs,
411
- durable_objects: configParam.durable_objects,
412
- r2_buckets: configParam.r2_buckets?.map(
413
- ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
414
- // same idea as kv namespace preview id,
415
- // same copy-on-write TODO
416
- if (!preview_bucket_name) {
417
- throw new Error(
418
- `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`
419
- );
420
- }
421
- return {
422
- binding,
423
- bucket_name: preview_bucket_name,
424
- };
425
- }
426
- ),
427
- worker_namespaces: configParam.worker_namespaces,
428
- services: configParam.services,
429
- unsafe: configParam.unsafe?.bindings,
430
- };
431
- }
432
-
433
427
  const getLocalPort = memoizeGetPort(DEFAULT_LOCAL_PORT);
434
- const getInspectorPort = memoizeGetPort(9229);
428
+ const getInspectorPort = memoizeGetPort(DEFAULT_INSPECTOR_PORT);
435
429
 
436
430
  // eslint-disable-next-line no-inner-declarations
437
431
  async function getDevReactElement(configParam: Config) {
438
432
  // now log all available bindings into the terminal
439
- const bindings = await getBindings(configParam);
433
+ const bindings = await getBindings(configParam, {
434
+ kv: args.kv,
435
+ vars: args.vars,
436
+ durableObjects: args.durableObjects,
437
+ r2: args.r2,
438
+ });
439
+
440
440
  // mask anything that was overridden in .dev.vars
441
441
  // so that we don't log potential secrets into the terminal
442
442
  const maskedVars = { ...bindings.vars };
@@ -483,16 +483,22 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
483
483
  jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
484
484
  tsconfig={args.tsconfig ?? config.tsconfig}
485
485
  upstreamProtocol={upstreamProtocol}
486
- localProtocol={args["local-protocol"] || config.dev.local_protocol}
486
+ localProtocol={args.localProtocol || config.dev.local_protocol}
487
487
  localUpstream={args["local-upstream"] || host}
488
488
  enableLocalPersistence={
489
- args["experimental-enable-local-persistence"] || false
489
+ args.experimentalEnableLocalPersistence || false
490
490
  }
491
+ liveReload={args.liveReload || false}
491
492
  accountId={config.account_id || getAccountFromCache()?.id}
492
493
  assetPaths={assetPaths}
494
+ assetsConfig={config.assets}
493
495
  port={args.port || config.dev.port || (await getLocalPort())}
494
496
  ip={args.ip || config.dev.ip}
495
- inspectorPort={args["inspector-port"] ?? (await getInspectorPort())}
497
+ inspectorPort={
498
+ args.inspectorPort ||
499
+ config.dev.inspector_port ||
500
+ (await getInspectorPort())
501
+ }
496
502
  isWorkersSite={Boolean(args.site || config.site)}
497
503
  compatibilityDate={getDevCompatibilityDate(
498
504
  config,
@@ -505,9 +511,14 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
505
511
  bindings={bindings}
506
512
  crons={config.triggers.crons}
507
513
  logLevel={args.logLevel}
514
+ logPrefix={args.logPrefix}
508
515
  onReady={args.onReady}
509
516
  inspect={args.inspect ?? true}
510
517
  showInteractiveDevSession={args.showInteractiveDevSession}
518
+ forceLocal={args.forceLocal}
519
+ enablePagesAssetsServiceBinding={args.enablePagesAssetsServiceBinding}
520
+ firstPartyWorker={config.first_party_worker}
521
+ sendMetrics={config.send_metrics}
511
522
  />
512
523
  );
513
524
  }
@@ -542,3 +553,73 @@ function memoizeGetPort(defaultPort: number) {
542
553
  return portValue || (portValue = await getPort({ port: defaultPort }));
543
554
  };
544
555
  }
556
+
557
+ async function getBindings(
558
+ configParam: Config,
559
+ args: AdditionalDevProps
560
+ ): Promise<CfWorkerInit["bindings"]> {
561
+ const bindings = {
562
+ kv_namespaces: [
563
+ ...(configParam.kv_namespaces || []).map(
564
+ ({ binding, preview_id, id: _id }) => {
565
+ // In `dev`, we make folks use a separate kv namespace called
566
+ // `preview_id` instead of `id` so that they don't
567
+ // break production data. So here we check that a `preview_id`
568
+ // has actually been configured.
569
+ // This whole block of code will be obsoleted in the future
570
+ // when we have copy-on-write for previews on edge workers.
571
+ if (!preview_id) {
572
+ // TODO: This error has to be a _lot_ better, ideally just asking
573
+ // to create a preview namespace for the user automatically
574
+ throw new Error(
575
+ `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
576
+ ); // Ugh, I really don't like this message very much
577
+ }
578
+ return {
579
+ binding,
580
+ id: preview_id,
581
+ };
582
+ }
583
+ ),
584
+ ...(args.kv || []),
585
+ ],
586
+ // Use a copy of combinedVars since we're modifying it later
587
+ vars: {
588
+ ...getVarsForDev(configParam),
589
+ ...args.vars,
590
+ },
591
+ wasm_modules: configParam.wasm_modules,
592
+ text_blobs: configParam.text_blobs,
593
+ data_blobs: configParam.data_blobs,
594
+ durable_objects: {
595
+ bindings: [
596
+ ...(configParam.durable_objects || { bindings: [] }).bindings,
597
+ ...(args.durableObjects || []),
598
+ ],
599
+ },
600
+ r2_buckets: [
601
+ ...(configParam.r2_buckets?.map(
602
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
603
+ // same idea as kv namespace preview id,
604
+ // same copy-on-write TODO
605
+ if (!preview_bucket_name) {
606
+ throw new Error(
607
+ `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`
608
+ );
609
+ }
610
+ return {
611
+ binding,
612
+ bucket_name: preview_bucket_name,
613
+ };
614
+ }
615
+ ) || []),
616
+ ...(args.r2 || []),
617
+ ],
618
+ worker_namespaces: configParam.worker_namespaces,
619
+ services: configParam.services,
620
+ unsafe: configParam.unsafe?.bindings,
621
+ logfwdr: configParam.logfwdr,
622
+ };
623
+
624
+ return bindings;
625
+ }
package/src/generate.ts CHANGED
@@ -1,33 +1,131 @@
1
- import { DeprecationError } from "./errors";
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { setup as createCloudflare } from "create-cloudflare";
4
+ import { initHandler } from "./init";
5
+ import { logger } from "./logger";
6
+ import { CommandLineArgsError, printWranglerBanner } from ".";
2
7
  import type { Argv, ArgumentsCamelCase } from "yargs";
3
8
 
9
+ // https://github.com/cloudflare/wrangler/blob/master/src/cli/mod.rs#L106-L123
4
10
  interface GenerateArgs {
5
- name: string;
6
- template: string;
11
+ name?: string;
12
+ template?: string;
13
+ type?: string;
14
+ site?: boolean;
7
15
  }
8
16
 
9
17
  export function generateOptions(yargs: Argv) {
10
18
  return yargs
11
19
  .positional("name", {
12
20
  describe: "Name of the Workers project",
13
- default: "worker",
21
+ type: "string",
14
22
  })
15
23
  .positional("template", {
24
+ type: "string",
16
25
  describe: "The URL of a GitHub template",
17
- default: "https://github.com/cloudflare/worker-template",
26
+ })
27
+ .option("type", {
28
+ alias: "t",
29
+ type: "string",
30
+ hidden: true,
31
+ deprecated: true,
32
+ })
33
+ .option("site", {
34
+ alias: "s",
35
+ type: "boolean",
36
+ hidden: true,
37
+ deprecated: true,
18
38
  });
19
39
  }
20
40
 
21
- export function generateHandler(
22
- generateArgs: ArgumentsCamelCase<GenerateArgs>
23
- ) {
24
- // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
25
- throw new DeprecationError(
26
- "`wrangler generate` has been deprecated.\n" +
27
- "Try running `wrangler init` to generate a basic Worker, or cloning the template repository instead:\n\n" +
41
+ export async function generateHandler({
42
+ // somehow, `init` marks name as required but then also runs fine
43
+ // with the name omitted, and then substitutes it at runtime with ""
44
+ name = "",
45
+ template,
46
+ type,
47
+ site,
48
+ ...args
49
+ }: ArgumentsCamelCase<GenerateArgs>) {
50
+ // delegate to `wrangler init` if no template is specified
51
+ if (template === undefined) {
52
+ return initHandler({ name, ...args });
53
+ }
54
+
55
+ // print down here cuz `init` prints it own its own
56
+ printWranglerBanner();
57
+
58
+ if (type) {
59
+ let message = "The --type option is no longer supported.";
60
+ if (args.type === "webpack") {
61
+ message +=
62
+ "\nIf you wish to use webpack then you will need to create a custom build.";
63
+ // TODO: Add a link to docs
64
+ }
65
+ throw new CommandLineArgsError(message);
66
+ }
67
+
68
+ const creationDirectory = generateWorkerDirectoryName(name);
69
+
70
+ if (site) {
71
+ const gitDirectory =
72
+ creationDirectory !== process.cwd()
73
+ ? path.basename(creationDirectory)
74
+ : "my-site";
75
+ const message =
76
+ "The --site option is no longer supported.\n" +
77
+ "If you wish to create a brand new Worker Sites project then clone the `worker-sites-template` starter repository:\n\n" +
28
78
  "```\n" +
29
- `git clone ${generateArgs.template}\n` +
79
+ `git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template ${gitDirectory}\n` +
80
+ `cd ${gitDirectory}\n` +
30
81
  "```\n\n" +
31
- "Please refer to https://developers.cloudflare.com/workers/wrangler/deprecations/#generate for more information."
82
+ "Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.\n" +
83
+ "Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/.";
84
+ throw new CommandLineArgsError(message);
85
+ }
86
+
87
+ logger.log(
88
+ `Creating a worker in ${path.basename(creationDirectory)} from ${template}`
32
89
  );
90
+
91
+ await createCloudflare(creationDirectory, template, {
92
+ init: true, // initialize a git repository
93
+ debug: logger.loggerLevel === "debug",
94
+ force: false, // do not overwrite an existing directory
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Creates a path based on the current working directory and a worker name.
100
+ * Automatically increments a counter when searching for an available directory.
101
+ *
102
+ * Running `wrangler generate worker https://some-git-repo` in a directory
103
+ * with the structure:
104
+ * ```
105
+ * - workers
106
+ * |
107
+ * | - worker
108
+ * | | - wrangler.toml
109
+ * | | ...
110
+ * |
111
+ * | - worker-1
112
+ * | | - wrangler.toml
113
+ * | | ...
114
+ * ```
115
+ *
116
+ * will result in a new worker called `worker-2` being generated.
117
+ *
118
+ * @param workerName the name of the generated worker
119
+ * @returns an absolute path to the directory to generate the worker into
120
+ */
121
+ function generateWorkerDirectoryName(workerName: string): string {
122
+ let workerDirectoryPath = path.resolve(process.cwd(), workerName);
123
+ let i = 1;
124
+
125
+ while (fs.existsSync(workerDirectoryPath)) {
126
+ workerDirectoryPath = path.resolve(process.cwd(), `${workerName}-${i}`);
127
+ i++;
128
+ }
129
+
130
+ return workerDirectoryPath;
33
131
  }