wrangler 2.0.7 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.tsx CHANGED
@@ -15,11 +15,11 @@ import { setGlobalDispatcher, ProxyAgent } from "undici";
15
15
  import makeCLI from "yargs";
16
16
  import { version as wranglerVersion } from "../package.json";
17
17
  import { fetchResult } from "./cfetch";
18
- import { findWranglerToml, readConfig } from "./config";
18
+ import { findWranglerToml, printBindings, readConfig } from "./config";
19
19
  import { createWorkerUploadForm } from "./create-worker-upload-form";
20
20
  import Dev from "./dev/dev";
21
21
  import { getVarsForDev } from "./dev/dev-vars";
22
- import { confirm, prompt } from "./dialogs";
22
+ import { confirm, prompt, select } from "./dialogs";
23
23
  import { getEntry } from "./entry";
24
24
  import { DeprecationError } from "./errors";
25
25
  import {
@@ -66,6 +66,7 @@ import {
66
66
  requireAuth,
67
67
  } from "./user";
68
68
  import { whoami } from "./whoami";
69
+ import { getZoneIdFromHost, getZoneForRoute } from "./zones";
69
70
 
70
71
  import type { Config } from "./config";
71
72
  import type { TailCLIFilters } from "./tail";
@@ -444,7 +445,10 @@ function createCLIParser(argv: string[]) {
444
445
  yesFlag ||
445
446
  (await confirm("Would you like to use git to manage this Worker?"));
446
447
  if (shouldInitGit) {
447
- await execa("git", ["init", "--initial-branch=main"], {
448
+ await execa("git", ["init"], {
449
+ cwd: creationDirectory,
450
+ });
451
+ await execa("git", ["branch", "-m", "main"], {
448
452
  cwd: creationDirectory,
449
453
  });
450
454
  await writeFile(
@@ -628,21 +632,21 @@ function createCLIParser(argv: string[]) {
628
632
  isWritingScripts: boolean,
629
633
  isCreatingWranglerToml: boolean,
630
634
  packagePath: string,
631
- scriptPath: string
635
+ scriptPath: string,
636
+ extraToml: TOML.JsonMap
632
637
  ) {
633
638
  if (isCreatingWranglerToml) {
634
- // rewrite wrangler.toml with main = "path/to/script"
639
+ // rewrite wrangler.toml with main = "path/to/script" and any additional config specified in `extraToml`
635
640
  const parsedWranglerToml = parseTOML(
636
641
  readFileSync(wranglerTomlDestination)
637
642
  );
638
- fs.writeFileSync(
639
- wranglerTomlDestination,
640
- TOML.stringify({
641
- name: parsedWranglerToml.name,
642
- main: scriptPath,
643
- compatibility_date: parsedWranglerToml.compatibility_date,
644
- })
645
- );
643
+ const newToml = {
644
+ name: parsedWranglerToml.name,
645
+ main: scriptPath,
646
+ compatibility_date: parsedWranglerToml.compatibility_date,
647
+ ...extraToml,
648
+ };
649
+ fs.writeFileSync(wranglerTomlDestination, TOML.stringify(newToml));
646
650
  }
647
651
  const isNamedWorker =
648
652
  isCreatingWranglerToml && path.dirname(packagePath) !== process.cwd();
@@ -658,7 +662,7 @@ function createCLIParser(argv: string[]) {
658
662
  start: isCreatingWranglerToml
659
663
  ? `wrangler dev`
660
664
  : `wrangler dev ${scriptPath}`,
661
- publish: isCreatingWranglerToml
665
+ deploy: isCreatingWranglerToml
662
666
  ? `wrangler publish`
663
667
  : `wrangler publish ${scriptPath}`,
664
668
  },
@@ -673,7 +677,7 @@ function createCLIParser(argv: string[]) {
673
677
  }npm start\``
674
678
  );
675
679
  instructions.push(
676
- `To publish your Worker to the Internet, run \`npm run publish\``
680
+ `To publish your Worker to the Internet, run \`npm run deploy\``
677
681
  );
678
682
  } else {
679
683
  instructions.push(
@@ -689,24 +693,75 @@ function createCLIParser(argv: string[]) {
689
693
  }
690
694
  }
691
695
 
696
+ async function getNewWorkerType(newWorkerFilename: string) {
697
+ return select(
698
+ `Would you like to create a Worker at ${newWorkerFilename}?`,
699
+ [
700
+ {
701
+ value: "none",
702
+ label: "None",
703
+ },
704
+ {
705
+ value: "fetch",
706
+ label: "Fetch handler",
707
+ },
708
+ {
709
+ value: "scheduled",
710
+ label: "Scheduled handler",
711
+ },
712
+ ],
713
+ 1
714
+ ) as Promise<"none" | "fetch" | "scheduled">;
715
+ }
716
+
717
+ function getNewWorkerTemplate(
718
+ lang: "js" | "ts",
719
+ workerType: "fetch" | "scheduled"
720
+ ) {
721
+ const templates = {
722
+ "js-fetch": "new-worker.js",
723
+ "js-scheduled": "new-worker-scheduled.js",
724
+ "ts-fetch": "new-worker.ts",
725
+ "ts-scheduled": "new-worker-scheduled.ts",
726
+ };
727
+
728
+ return templates[`${lang}-${workerType}`];
729
+ }
730
+
731
+ function getNewWorkerToml(
732
+ workerType: "fetch" | "scheduled"
733
+ ): TOML.JsonMap {
734
+ if (workerType === "scheduled") {
735
+ return {
736
+ triggers: {
737
+ crons: ["1 * * * *"],
738
+ },
739
+ };
740
+ }
741
+
742
+ return {};
743
+ }
744
+
692
745
  if (isTypescriptProject) {
693
746
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.ts"))) {
694
- const shouldCreateSource =
695
- yesFlag ||
696
- (await confirm(
697
- `Would you like to create a Worker at ${path.relative(
698
- process.cwd(),
699
- path.join(creationDirectory, "./src/index.ts")
700
- )}?`
701
- ));
747
+ const newWorkerFilename = path.relative(
748
+ process.cwd(),
749
+ path.join(creationDirectory, "./src/index.ts")
750
+ );
751
+
752
+ const newWorkerType = yesFlag
753
+ ? "fetch"
754
+ : await getNewWorkerType(newWorkerFilename);
755
+
756
+ if (newWorkerType !== "none") {
757
+ const template = getNewWorkerTemplate("ts", newWorkerType);
702
758
 
703
- if (shouldCreateSource) {
704
759
  await mkdir(path.join(creationDirectory, "./src"), {
705
760
  recursive: true,
706
761
  });
707
762
  await writeFile(
708
763
  path.join(creationDirectory, "./src/index.ts"),
709
- readFileSync(path.join(__dirname, "../templates/new-worker.ts"))
764
+ readFileSync(path.join(__dirname, `../templates/${template}`))
710
765
  );
711
766
 
712
767
  logger.log(
@@ -720,28 +775,31 @@ function createCLIParser(argv: string[]) {
720
775
  shouldWritePackageJsonScripts,
721
776
  justCreatedWranglerToml,
722
777
  pathToPackageJson,
723
- "src/index.ts"
778
+ "src/index.ts",
779
+ getNewWorkerToml(newWorkerType)
724
780
  );
725
781
  }
726
782
  }
727
783
  } else {
728
784
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.js"))) {
729
- const shouldCreateSource =
730
- yesFlag ||
731
- (await confirm(
732
- `Would you like to create a Worker at ${path.relative(
733
- process.cwd(),
734
- path.join(creationDirectory, "./src/index.js")
735
- )}?`
736
- ));
785
+ const newWorkerFilename = path.relative(
786
+ process.cwd(),
787
+ path.join(creationDirectory, "./src/index.js")
788
+ );
789
+
790
+ const newWorkerType = yesFlag
791
+ ? "fetch"
792
+ : await getNewWorkerType(newWorkerFilename);
793
+
794
+ if (newWorkerType !== "none") {
795
+ const template = getNewWorkerTemplate("js", newWorkerType);
737
796
 
738
- if (shouldCreateSource) {
739
797
  await mkdir(path.join(creationDirectory, "./src"), {
740
798
  recursive: true,
741
799
  });
742
800
  await writeFile(
743
801
  path.join(creationDirectory, "./src/index.js"),
744
- readFileSync(path.join(__dirname, "../templates/new-worker.js"))
802
+ readFileSync(path.join(__dirname, `../templates/${template}`))
745
803
  );
746
804
 
747
805
  logger.log(
@@ -755,7 +813,8 @@ function createCLIParser(argv: string[]) {
755
813
  shouldWritePackageJsonScripts,
756
814
  justCreatedWranglerToml,
757
815
  pathToPackageJson,
758
- "src/index.js"
816
+ "src/index.js",
817
+ getNewWorkerToml(newWorkerType)
759
818
  );
760
819
  }
761
820
  }
@@ -1024,92 +1083,25 @@ function createCLIParser(argv: string[]) {
1024
1083
  );
1025
1084
  }
1026
1085
 
1027
- const accountId = !args.local ? await requireAuth(config) : undefined;
1028
-
1029
1086
  // TODO: if worker_dev = false and no routes, then error (only for dev)
1030
1087
 
1031
- /**
1032
- * Given something that resembles a URL,
1033
- * try to extract a host from it
1034
- */
1035
- function getHost(urlLike: string): string | undefined {
1036
- // strip leading * / *.
1037
- urlLike = urlLike.replace(/^\*(\.)?/g, "");
1038
-
1039
- if (
1040
- !(urlLike.startsWith("http://") || urlLike.startsWith("https://"))
1041
- ) {
1042
- urlLike = "http://" + urlLike;
1043
- }
1044
- return new URL(urlLike).host;
1045
- }
1046
-
1047
- /**
1048
- * Given something that resembles a host,
1049
- * try to infer a zone id from it
1050
- */
1051
- async function getZoneId(host: string): Promise<string | undefined> {
1052
- const zones = await fetchResult<{ id: string }[]>(
1053
- `/zones`,
1054
- {},
1055
- new URLSearchParams({ name: host })
1056
- );
1057
- return zones[0]?.id;
1058
- }
1059
-
1060
- // When we're given a host (in one of the above ways), we do 2 things:
1061
- // - We try to extract a host from it
1062
- // - We try to get a zone id from the host
1063
- //
1064
- // So it turns out it's particularly hard to get a 'valid' domain
1065
- // from a string, so we don't even try to validate TLDs, etc.
1066
- // Once we get something that looks like w.x.y.z-ish, we then try to
1067
- // get a zone id for it, by lopping off subdomains until we get a hit
1068
- // from the API. That's it!
1069
-
1070
- let zone: { host: string; id: string } | undefined;
1088
+ // Compute zone info from the `host` and `route` args and config;
1089
+ let host = args.host || config.dev.host;
1090
+ let zoneId: string | undefined;
1071
1091
 
1072
1092
  if (!args.local) {
1073
- const hostLike =
1074
- args.host ||
1075
- config.dev.host ||
1076
- (args.routes && args.routes[0]) ||
1077
- config.route ||
1078
- (config.routes && config.routes[0]);
1079
-
1080
- let zoneId: string | undefined =
1081
- typeof hostLike === "object" && "zone_id" in hostLike
1082
- ? hostLike.zone_id
1083
- : undefined;
1084
-
1085
- const host =
1086
- typeof hostLike === "string"
1087
- ? getHost(hostLike)
1088
- : typeof hostLike === "object"
1089
- ? "zone_name" in hostLike
1090
- ? getHost(hostLike.zone_name)
1091
- : getHost(hostLike.pattern)
1092
- : undefined;
1093
-
1094
- const hostPieces =
1095
- typeof host === "string" ? host.split(".") : undefined;
1096
-
1097
- while (!zoneId && hostPieces && hostPieces.length > 1) {
1098
- zoneId = await getZoneId(hostPieces.join("."));
1099
- hostPieces.shift();
1093
+ if (host) {
1094
+ zoneId = await getZoneIdFromHost(host);
1100
1095
  }
1101
-
1102
- if (host && !zoneId) {
1103
- throw new Error(`Could not find zone for ${hostLike}`);
1096
+ const routes = args.routes || config.route || config.routes;
1097
+ if (!zoneId && routes) {
1098
+ const firstRoute = Array.isArray(routes) ? routes[0] : routes;
1099
+ const zone = await getZoneForRoute(firstRoute);
1100
+ if (zone) {
1101
+ zoneId = zone.id;
1102
+ host = zone.host;
1103
+ }
1104
1104
  }
1105
-
1106
- zone =
1107
- typeof zoneId === "string" && typeof host === "string"
1108
- ? {
1109
- host,
1110
- id: zoneId,
1111
- }
1112
- : undefined;
1113
1105
  }
1114
1106
 
1115
1107
  const nodeCompat = args.nodeCompat ?? config.node_compat;
@@ -1119,12 +1111,77 @@ function createCLIParser(argv: string[]) {
1119
1111
  );
1120
1112
  }
1121
1113
 
1114
+ const bindings = {
1115
+ kv_namespaces: config.kv_namespaces?.map(
1116
+ ({ binding, preview_id, id: _id }) => {
1117
+ // In `dev`, we make folks use a separate kv namespace called
1118
+ // `preview_id` instead of `id` so that they don't
1119
+ // break production data. So here we check that a `preview_id`
1120
+ // has actually been configured.
1121
+ // This whole block of code will be obsoleted in the future
1122
+ // when we have copy-on-write for previews on edge workers.
1123
+ if (!preview_id) {
1124
+ // TODO: This error has to be a _lot_ better, ideally just asking
1125
+ // to create a preview namespace for the user automatically
1126
+ throw new Error(
1127
+ `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`
1128
+ ); // Ugh, I really don't like this message very much
1129
+ }
1130
+ return {
1131
+ binding,
1132
+ id: preview_id,
1133
+ };
1134
+ }
1135
+ ),
1136
+ // Use a copy of combinedVars since we're modifying it later
1137
+ vars: getVarsForDev(config),
1138
+ wasm_modules: config.wasm_modules,
1139
+ text_blobs: config.text_blobs,
1140
+ data_blobs: config.data_blobs,
1141
+ durable_objects: config.durable_objects,
1142
+ r2_buckets: config.r2_buckets?.map(
1143
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1144
+ // same idea as kv namespace preview id,
1145
+ // same copy-on-write TODO
1146
+ if (!preview_bucket_name) {
1147
+ throw new Error(
1148
+ `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`
1149
+ );
1150
+ }
1151
+ return {
1152
+ binding,
1153
+ bucket_name: preview_bucket_name,
1154
+ };
1155
+ }
1156
+ ),
1157
+ services: config.services,
1158
+ unsafe: config.unsafe?.bindings,
1159
+ };
1160
+
1161
+ // mask anything that was overridden in .dev.vars
1162
+ // so that we don't log potential secrets into the terminal
1163
+ const maskedVars = { ...bindings.vars };
1164
+ for (const key of Object.keys(maskedVars)) {
1165
+ if (maskedVars[key] !== config.vars[key]) {
1166
+ // This means it was overridden in .dev.vars
1167
+ // so let's mask it
1168
+ maskedVars[key] = "(hidden)";
1169
+ }
1170
+ }
1171
+
1172
+ // now log all available bindings into the terminal
1173
+ printBindings({
1174
+ ...bindings,
1175
+ vars: maskedVars,
1176
+ });
1177
+
1122
1178
  const { waitUntilExit } = render(
1123
1179
  <Dev
1124
1180
  name={getScriptName(args, config)}
1125
1181
  entry={entry}
1126
1182
  env={args.env}
1127
- zone={zone}
1183
+ zone={zoneId}
1184
+ host={host}
1128
1185
  rules={getRules(config)}
1129
1186
  legacyEnv={isLegacyEnv(config)}
1130
1187
  minify={args.minify ?? config.minify}
@@ -1139,7 +1196,7 @@ function createCLIParser(argv: string[]) {
1139
1196
  enableLocalPersistence={
1140
1197
  args["experimental-enable-local-persistence"] || false
1141
1198
  }
1142
- accountId={accountId}
1199
+ accountId={config.account_id}
1143
1200
  assetPaths={getAssetPaths(
1144
1201
  config,
1145
1202
  args.site,
@@ -1164,51 +1221,7 @@ function createCLIParser(argv: string[]) {
1164
1221
  args["compatibility-flags"] || config.compatibility_flags
1165
1222
  }
1166
1223
  usageModel={config.usage_model}
1167
- bindings={{
1168
- kv_namespaces: config.kv_namespaces?.map(
1169
- ({ binding, preview_id, id: _id }) => {
1170
- // In `dev`, we make folks use a separate kv namespace called
1171
- // `preview_id` instead of `id` so that they don't
1172
- // break production data. So here we check that a `preview_id`
1173
- // has actually been configured.
1174
- // This whole block of code will be obsoleted in the future
1175
- // when we have copy-on-write for previews on edge workers.
1176
- if (!preview_id) {
1177
- // TODO: This error has to be a _lot_ better, ideally just asking
1178
- // to create a preview namespace for the user automatically
1179
- throw new Error(
1180
- `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`
1181
- ); // Ugh, I really don't like this message very much
1182
- }
1183
- return {
1184
- binding,
1185
- id: preview_id,
1186
- };
1187
- }
1188
- ),
1189
- vars: getVarsForDev(config),
1190
- wasm_modules: config.wasm_modules,
1191
- text_blobs: config.text_blobs,
1192
- data_blobs: config.data_blobs,
1193
- durable_objects: config.durable_objects,
1194
- r2_buckets: config.r2_buckets?.map(
1195
- ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1196
- // same idea as kv namespace preview id,
1197
- // same copy-on-write TODO
1198
- if (!preview_bucket_name) {
1199
- throw new Error(
1200
- `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`
1201
- );
1202
- }
1203
- return {
1204
- binding,
1205
- bucket_name: preview_bucket_name,
1206
- };
1207
- }
1208
- ),
1209
- services: config.services,
1210
- unsafe: config.unsafe?.bindings,
1211
- }}
1224
+ bindings={bindings}
1212
1225
  crons={config.triggers.crons}
1213
1226
  />
1214
1227
  );
@@ -1597,6 +1610,7 @@ function createCLIParser(argv: string[]) {
1597
1610
  rules={getRules(config)}
1598
1611
  env={args.env}
1599
1612
  zone={undefined}
1613
+ host={undefined}
1600
1614
  legacyEnv={isLegacyEnv(config)}
1601
1615
  build={config.build || {}}
1602
1616
  minify={undefined}
@@ -2752,14 +2766,14 @@ export async function main(argv: string[]): Promise<void> {
2752
2766
  await createCLIParser([...argv, "--help"]).parse();
2753
2767
  } else if (e instanceof ParseError) {
2754
2768
  e.notes.push({
2755
- text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new",
2769
+ text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new/choose",
2756
2770
  });
2757
- logger.error(formatMessage(e));
2771
+ logger.log(formatMessage(e));
2758
2772
  } else {
2759
2773
  logger.error(e instanceof Error ? e.message : e);
2760
2774
  logger.log(
2761
2775
  `${fgGreenColor}%s${resetColor}`,
2762
- "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2776
+ "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
2763
2777
  );
2764
2778
  }
2765
2779
  throw e;
@@ -5,15 +5,20 @@ import { logger } from "./logger";
5
5
 
6
6
  export interface PackageManager {
7
7
  cwd: string;
8
- type: "npm" | "yarn";
8
+ type: "npm" | "yarn" | "pnpm";
9
9
  addDevDeps(...packages: string[]): Promise<void>;
10
10
  install(): Promise<void>;
11
11
  }
12
12
 
13
13
  export async function getPackageManager(cwd: string): Promise<PackageManager> {
14
- const [hasYarn, hasNpm] = await Promise.all([supportsYarn(), supportsNpm()]);
14
+ const [hasYarn, hasNpm, hasPnpm] = await Promise.all([
15
+ supportsYarn(),
16
+ supportsNpm(),
17
+ supportsPnpm(),
18
+ ]);
15
19
  const hasYarnLock = existsSync(join(cwd, "yarn.lock"));
16
20
  const hasNpmLock = existsSync(join(cwd, "package-lock.json"));
21
+ const hasPnpmLock = existsSync(join(cwd, "pnpm-lock.yaml"));
17
22
 
18
23
  if (hasNpmLock) {
19
24
  if (hasNpm) {
@@ -28,6 +33,18 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
28
33
  );
29
34
  return { ...YarnPackageManager, cwd };
30
35
  }
36
+ } else if (hasPnpmLock) {
37
+ if (hasPnpm) {
38
+ logger.log(
39
+ "Using pnpm as package manager, as there is already a pnpm-lock.yaml file."
40
+ );
41
+ return { ...PnpmPackageManager, cwd };
42
+ } else {
43
+ logger.warn(
44
+ "There is already a pnpm-lock.yaml file but could not find pnpm on the PATH."
45
+ );
46
+ // will simply fallback to the first found of [npm, yaml, pnpm] in the next if round.
47
+ }
31
48
  } else if (hasYarnLock) {
32
49
  if (hasYarn) {
33
50
  logger.log(
@@ -49,9 +66,12 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
49
66
  } else if (hasYarn) {
50
67
  logger.log("Using yarn as package manager.");
51
68
  return { ...YarnPackageManager, cwd };
69
+ } else if (hasPnpm) {
70
+ logger.log("Using pnpm as package manager.");
71
+ return { ...PnpmPackageManager, cwd };
52
72
  } else {
53
73
  throw new Error(
54
- "Unable to find a package manager. Supported managers are: npm and yarn."
74
+ "Unable to find a package manager. Supported managers are: npm, yarn, and pnpm."
55
75
  );
56
76
  }
57
77
  }
@@ -86,6 +106,29 @@ const NpmPackageManager: PackageManager = {
86
106
  },
87
107
  };
88
108
 
109
+ /**
110
+ * Manage packages using pnpm
111
+ */
112
+ const PnpmPackageManager: PackageManager = {
113
+ cwd: process.cwd(),
114
+ type: "pnpm",
115
+ /** Add and install a new devDependency into the local package.json. */
116
+ async addDevDeps(...packages: string[]): Promise<void> {
117
+ await execa("pnpm", ["install", ...packages, "--save-dev"], {
118
+ stdio: "inherit",
119
+ cwd: this.cwd,
120
+ });
121
+ },
122
+
123
+ /** Install all the dependencies in the local package.json. */
124
+ async install(): Promise<void> {
125
+ await execa("pnpm", ["install"], {
126
+ stdio: "inherit",
127
+ cwd: this.cwd,
128
+ });
129
+ },
130
+ };
131
+
89
132
  /**
90
133
  * Manage packages using yarn
91
134
  */
@@ -125,3 +168,7 @@ function supportsYarn(): Promise<boolean> {
125
168
  function supportsNpm(): Promise<boolean> {
126
169
  return supports("npm");
127
170
  }
171
+
172
+ function supportsPnpm(): Promise<boolean> {
173
+ return supports("pnpm");
174
+ }