wrangler 2.0.21 → 2.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +527 -5
  4. package/package.json +18 -5
  5. package/src/__tests__/configuration.test.ts +88 -16
  6. package/src/__tests__/dev.test.tsx +95 -4
  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 +35 -0
  13. package/src/__tests__/publish.test.ts +573 -254
  14. package/src/__tests__/r2.test.ts +155 -71
  15. package/src/__tests__/user.test.ts +1 -0
  16. package/src/__tests__/validate-dev-props.test.ts +56 -0
  17. package/src/__tests__/version.test.ts +35 -0
  18. package/src/__tests__/whoami.test.tsx +60 -1
  19. package/src/api/dev.ts +43 -9
  20. package/src/bundle.ts +297 -37
  21. package/src/cfetch/internal.ts +34 -2
  22. package/src/config/config.ts +14 -2
  23. package/src/config/environment.ts +40 -8
  24. package/src/config/index.ts +13 -0
  25. package/src/config/validation.ts +110 -8
  26. package/src/create-worker-preview.ts +3 -1
  27. package/src/create-worker-upload-form.ts +25 -0
  28. package/src/dev/dev.tsx +135 -31
  29. package/src/dev/local.tsx +48 -20
  30. package/src/dev/remote.tsx +39 -12
  31. package/src/dev/use-esbuild.ts +25 -0
  32. package/src/dev/validate-dev-props.ts +31 -0
  33. package/src/dev-registry.tsx +157 -0
  34. package/src/dev.tsx +137 -65
  35. package/src/generate.ts +112 -14
  36. package/src/index.tsx +222 -7
  37. package/src/inspect.ts +93 -5
  38. package/src/metrics/index.ts +1 -0
  39. package/src/metrics/is-ci.ts +14 -0
  40. package/src/metrics/metrics-config.ts +19 -2
  41. package/src/metrics/metrics-dispatcher.ts +1 -0
  42. package/src/metrics/metrics-usage-headers.ts +24 -0
  43. package/src/metrics/send-event.ts +2 -2
  44. package/src/miniflare-cli/assets.ts +543 -0
  45. package/src/miniflare-cli/index.ts +36 -4
  46. package/src/module-collection.ts +3 -3
  47. package/src/pages/constants.ts +1 -0
  48. package/src/pages/deployments.tsx +1 -1
  49. package/src/pages/dev.tsx +85 -639
  50. package/src/pages/publish.tsx +1 -1
  51. package/src/pages/upload.tsx +32 -13
  52. package/src/publish.ts +139 -112
  53. package/src/r2.ts +68 -0
  54. package/src/user/choose-account.tsx +20 -11
  55. package/src/user/user.tsx +20 -2
  56. package/src/whoami.tsx +79 -1
  57. package/src/worker.ts +12 -0
  58. package/templates/first-party-worker-module-facade.ts +18 -0
  59. package/templates/format-dev-errors.ts +32 -0
  60. package/templates/pages-shim.ts +9 -0
  61. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  62. package/templates/service-bindings-module-facade.js +51 -0
  63. package/templates/service-bindings-sw-facade.js +39 -0
  64. package/wrangler-dist/cli.d.ts +32 -3
  65. package/wrangler-dist/cli.js +45257 -25209
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
 
@@ -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,31 @@ 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
+ };
279
+ type StartDevOptions = ArgumentsCamelCase<DevArgs> &
280
+ // These options can be passed in directly when called with the `wrangler.dev()` API.
281
+ // They aren't exposed as CLI arguments.
282
+ AdditionalDevProps & {
283
+ forceLocal?: boolean;
284
+ enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
285
+ };
286
+
287
+ export async function startDev(args: StartDevOptions) {
254
288
  let watcher: ReturnType<typeof watch> | undefined;
255
289
  let rerender: (node: React.ReactNode) => void | undefined;
256
290
  try {
@@ -265,11 +299,6 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
265
299
  ((args.script &&
266
300
  findWranglerToml(path.dirname(args.script))) as ConfigPath);
267
301
  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
302
 
274
303
  if (config.configPath) {
275
304
  watcher = watch(config.configPath, {
@@ -291,6 +320,15 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
291
320
  "dev"
292
321
  );
293
322
 
323
+ await metrics.sendMetricsEvent(
324
+ "run dev",
325
+ {
326
+ local: args.local,
327
+ usesTypeScript: /\.tsx?$/.test(entry.file),
328
+ },
329
+ { sendMetrics: config.send_metrics, offline: args.local }
330
+ );
331
+
294
332
  if (config.services && config.services.length > 0) {
295
333
  logger.warn(
296
334
  `This worker is bound to live services: ${config.services
@@ -351,6 +389,10 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
351
389
  const routes: Route[] | undefined =
352
390
  args.routes || (config.route && [config.route]) || config.routes;
353
391
 
392
+ if (args.forceLocal) {
393
+ args.local = true;
394
+ }
395
+
354
396
  if (!args.local) {
355
397
  if (host) {
356
398
  zoneId = await getZoneIdFromHost(host);
@@ -370,73 +412,25 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
370
412
  }
371
413
  }
372
414
 
373
- const nodeCompat = args["node-compat"] ?? config.node_compat;
415
+ const nodeCompat = args.nodeCompat ?? config.node_compat;
374
416
  if (nodeCompat) {
375
417
  logger.warn(
376
418
  "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
419
  );
378
420
  }
379
421
 
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
422
  const getLocalPort = memoizeGetPort(DEFAULT_LOCAL_PORT);
434
- const getInspectorPort = memoizeGetPort(9229);
423
+ const getInspectorPort = memoizeGetPort(DEFAULT_INSPECTOR_PORT);
435
424
 
436
425
  // eslint-disable-next-line no-inner-declarations
437
426
  async function getDevReactElement(configParam: Config) {
438
427
  // now log all available bindings into the terminal
439
- const bindings = await getBindings(configParam);
428
+ const bindings = await getBindings(configParam, {
429
+ kv: args.kv,
430
+ vars: args.vars,
431
+ durableObjects: args.durableObjects,
432
+ });
433
+
440
434
  // mask anything that was overridden in .dev.vars
441
435
  // so that we don't log potential secrets into the terminal
442
436
  const maskedVars = { ...bindings.vars };
@@ -483,16 +477,22 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
483
477
  jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
484
478
  tsconfig={args.tsconfig ?? config.tsconfig}
485
479
  upstreamProtocol={upstreamProtocol}
486
- localProtocol={args["local-protocol"] || config.dev.local_protocol}
480
+ localProtocol={args.localProtocol || config.dev.local_protocol}
487
481
  localUpstream={args["local-upstream"] || host}
488
482
  enableLocalPersistence={
489
- args["experimental-enable-local-persistence"] || false
483
+ args.experimentalEnableLocalPersistence || false
490
484
  }
485
+ liveReload={args.liveReload || false}
491
486
  accountId={config.account_id || getAccountFromCache()?.id}
492
487
  assetPaths={assetPaths}
488
+ assetsConfig={config.assets}
493
489
  port={args.port || config.dev.port || (await getLocalPort())}
494
490
  ip={args.ip || config.dev.ip}
495
- inspectorPort={args["inspector-port"] ?? (await getInspectorPort())}
491
+ inspectorPort={
492
+ args["inspector-port"] ||
493
+ config.dev.inspector_port ||
494
+ (await getInspectorPort())
495
+ }
496
496
  isWorkersSite={Boolean(args.site || config.site)}
497
497
  compatibilityDate={getDevCompatibilityDate(
498
498
  config,
@@ -505,9 +505,14 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
505
505
  bindings={bindings}
506
506
  crons={config.triggers.crons}
507
507
  logLevel={args.logLevel}
508
+ logPrefix={args.logPrefix}
508
509
  onReady={args.onReady}
509
510
  inspect={args.inspect ?? true}
510
511
  showInteractiveDevSession={args.showInteractiveDevSession}
512
+ forceLocal={args.forceLocal}
513
+ enablePagesAssetsServiceBinding={args.enablePagesAssetsServiceBinding}
514
+ firstPartyWorker={config.first_party_worker}
515
+ sendMetrics={config.send_metrics}
511
516
  />
512
517
  );
513
518
  }
@@ -542,3 +547,70 @@ function memoizeGetPort(defaultPort: number) {
542
547
  return portValue || (portValue = await getPort({ port: defaultPort }));
543
548
  };
544
549
  }
550
+
551
+ async function getBindings(
552
+ configParam: Config,
553
+ args: AdditionalDevProps
554
+ ): Promise<CfWorkerInit["bindings"]> {
555
+ const bindings = {
556
+ kv_namespaces: [
557
+ ...(configParam.kv_namespaces || []).map(
558
+ ({ binding, preview_id, id: _id }) => {
559
+ // In `dev`, we make folks use a separate kv namespace called
560
+ // `preview_id` instead of `id` so that they don't
561
+ // break production data. So here we check that a `preview_id`
562
+ // has actually been configured.
563
+ // This whole block of code will be obsoleted in the future
564
+ // when we have copy-on-write for previews on edge workers.
565
+ if (!preview_id) {
566
+ // TODO: This error has to be a _lot_ better, ideally just asking
567
+ // to create a preview namespace for the user automatically
568
+ throw new Error(
569
+ `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`
570
+ ); // Ugh, I really don't like this message very much
571
+ }
572
+ return {
573
+ binding,
574
+ id: preview_id,
575
+ };
576
+ }
577
+ ),
578
+ ...(args.kv || []),
579
+ ],
580
+ // Use a copy of combinedVars since we're modifying it later
581
+ vars: {
582
+ ...getVarsForDev(configParam),
583
+ ...args.vars,
584
+ },
585
+ wasm_modules: configParam.wasm_modules,
586
+ text_blobs: configParam.text_blobs,
587
+ data_blobs: configParam.data_blobs,
588
+ durable_objects: {
589
+ bindings: [
590
+ ...(configParam.durable_objects || { bindings: [] }).bindings,
591
+ ...(args.durableObjects || []),
592
+ ],
593
+ },
594
+ r2_buckets: configParam.r2_buckets?.map(
595
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
596
+ // same idea as kv namespace preview id,
597
+ // same copy-on-write TODO
598
+ if (!preview_bucket_name) {
599
+ throw new Error(
600
+ `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`
601
+ );
602
+ }
603
+ return {
604
+ binding,
605
+ bucket_name: preview_bucket_name,
606
+ };
607
+ }
608
+ ),
609
+ worker_namespaces: configParam.worker_namespaces,
610
+ services: configParam.services,
611
+ unsafe: configParam.unsafe?.bindings,
612
+ logfwdr: configParam.logfwdr,
613
+ };
614
+
615
+ return bindings;
616
+ }
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
  }