wrangler 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/bin/wrangler.js +9 -1
  2. package/miniflare-dist/index.mjs +1 -1
  3. package/package.json +12 -10
  4. package/src/__tests__/api-dev.test.ts +65 -36
  5. package/src/__tests__/api-devregistry.test.js +14 -6
  6. package/src/__tests__/configuration.test.ts +2 -31
  7. package/src/__tests__/{d1.test.ts → d1/d1.test.ts} +48 -5
  8. package/src/__tests__/d1/splitter.test.ts +255 -0
  9. package/src/__tests__/delete.test.ts +5 -2
  10. package/src/__tests__/deployments.test.ts +20 -6
  11. package/src/__tests__/dev.test.tsx +52 -19
  12. package/src/__tests__/generate.test.ts +7 -4
  13. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  14. package/src/__tests__/helpers/mock-cfetch.ts +2 -57
  15. package/src/__tests__/helpers/mock-dialogs.ts +70 -86
  16. package/src/__tests__/helpers/mock-oauth-flow.ts +64 -49
  17. package/src/__tests__/helpers/mock-process.ts +8 -13
  18. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  19. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  20. package/src/__tests__/index.test.ts +46 -42
  21. package/src/__tests__/init.test.ts +782 -522
  22. package/src/__tests__/jest.setup.ts +20 -24
  23. package/src/__tests__/kv.test.ts +286 -173
  24. package/src/__tests__/logout.test.ts +1 -1
  25. package/src/__tests__/metrics.test.ts +5 -7
  26. package/src/__tests__/middleware.scheduled.test.ts +40 -30
  27. package/src/__tests__/middleware.test.ts +144 -120
  28. package/src/__tests__/pages.test.ts +1618 -1161
  29. package/src/__tests__/publish.test.ts +174 -125
  30. package/src/__tests__/r2.test.ts +2 -2
  31. package/src/__tests__/secret.test.ts +183 -126
  32. package/src/__tests__/tail.test.ts +6 -0
  33. package/src/__tests__/tsconfig-sanity.ts +12 -0
  34. package/src/__tests__/tsconfig.json +8 -0
  35. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  36. package/src/__tests__/whoami.test.tsx +1 -96
  37. package/src/api/dev.ts +78 -41
  38. package/src/api/index.ts +1 -1
  39. package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  40. package/src/cfetch/index.ts +0 -2
  41. package/src/cfetch/internal.ts +16 -18
  42. package/src/cli.ts +2 -2
  43. package/src/config/index.ts +2 -1
  44. package/src/config/validation.ts +1 -2
  45. package/src/create-worker-upload-form.ts +2 -2
  46. package/src/d1/{delete.tsx → delete.ts} +0 -0
  47. package/src/d1/execute.tsx +8 -37
  48. package/src/d1/migrations/apply.tsx +32 -19
  49. package/src/d1/migrations/{index.tsx → index.ts} +0 -0
  50. package/src/d1/splitter.ts +161 -0
  51. package/src/d1/{types.tsx → types.ts} +0 -0
  52. package/src/delete.ts +3 -8
  53. package/src/deployments.ts +6 -0
  54. package/src/deprecated/index.ts +2 -295
  55. package/src/dev/dev.tsx +2 -2
  56. package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  57. package/src/dev/local.tsx +16 -4
  58. package/src/dev/remote.tsx +28 -1
  59. package/src/dev/start-server.ts +19 -11
  60. package/src/dev/use-esbuild.ts +1 -1
  61. package/src/{dev-registry.tsx → dev-registry.ts} +0 -0
  62. package/src/dev.tsx +35 -11
  63. package/src/dialogs.ts +136 -0
  64. package/src/dispatch-namespace.ts +1 -1
  65. package/src/docs/index.ts +97 -0
  66. package/src/environment-variables/factory.ts +88 -0
  67. package/src/environment-variables/misc-variables.ts +30 -0
  68. package/src/generate/index.ts +300 -0
  69. package/src/{index.tsx → index.ts} +16 -10
  70. package/src/init.ts +106 -60
  71. package/src/jest.d.ts +4 -0
  72. package/src/logger.ts +15 -3
  73. package/src/metrics/metrics-config.ts +1 -1
  74. package/src/metrics/send-event.ts +2 -1
  75. package/src/miniflare-cli/assets.ts +4 -0
  76. package/src/miniflare-cli/index.ts +1 -5
  77. package/src/miniflare-cli/tsconfig.json +9 -0
  78. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  79. package/src/miniflare-cli/types.ts +11 -0
  80. package/src/pages/{build.tsx → build.ts} +0 -0
  81. package/src/pages/{deployment-tails.tsx → deployment-tails.ts} +0 -0
  82. package/src/pages/{dev.tsx → dev.ts} +53 -55
  83. package/src/pages/functions/buildWorker.ts +1 -1
  84. package/src/pages/functions/tsconfig.json +8 -0
  85. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  86. package/src/pages/{functions.tsx → functions.ts} +0 -0
  87. package/src/pages/{hash.tsx → hash.ts} +0 -0
  88. package/src/pages/{index.tsx → index.ts} +0 -0
  89. package/src/pages/projects.tsx +3 -5
  90. package/src/pages/publish.tsx +16 -5
  91. package/src/pages/upload.tsx +27 -6
  92. package/src/publish/publish.ts +9 -7
  93. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +1 -1
  94. package/src/secret/index.ts +1 -1
  95. package/src/{sites.tsx → sites.ts} +0 -0
  96. package/src/tail/index.ts +2 -3
  97. package/src/tsconfig-sanity.ts +16 -0
  98. package/src/user/access.ts +0 -1
  99. package/src/user/auth-variables.ts +113 -0
  100. package/src/user/choose-account.tsx +1 -31
  101. package/src/user/index.ts +0 -1
  102. package/src/user/{user.tsx → user.ts} +107 -73
  103. package/src/{whoami.tsx → whoami.ts} +37 -71
  104. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  105. package/templates/__tests__/tsconfig.json +8 -0
  106. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  107. package/templates/d1-beta-facade.js +36 -0
  108. package/templates/facade.d.ts +14 -0
  109. package/templates/first-party-worker-module-facade.ts +4 -3
  110. package/templates/format-dev-errors.ts +7 -6
  111. package/templates/init-tests/test-jest-new-worker.js +3 -5
  112. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  113. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  114. package/templates/middleware/loader-modules.ts +0 -2
  115. package/templates/middleware/loader-sw.ts +6 -0
  116. package/templates/pages-dev-pipeline.ts +4 -1
  117. package/templates/pages-shim.ts +4 -1
  118. package/templates/pages-template-plugin.ts +12 -7
  119. package/templates/serve-static-assets.ts +16 -14
  120. package/templates/tsconfig-sanity.ts +11 -0
  121. package/templates/tsconfig.init.json +106 -0
  122. package/templates/tsconfig.json +5 -103
  123. package/templates/tsconfig.tsbuildinfo +1 -0
  124. package/wrangler-dist/cli.d.ts +58 -60
  125. package/wrangler-dist/cli.js +34498 -55459
  126. package/wrangler-dist/wasm-sync.wasm +0 -0
  127. package/src/__tests__/dialogs.test.tsx +0 -40
  128. package/src/dialogs.tsx +0 -168
  129. package/src/environment-variables.ts +0 -50
  130. package/src/user/env-vars.ts +0 -46
@@ -0,0 +1,300 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { cloneIntoDirectory, initializeGit } from "../git-client";
4
+ import { CommandLineArgsError, printWranglerBanner } from "../index";
5
+ import { initHandler } from "../init";
6
+ import { logger } from "../logger";
7
+ import type {
8
+ CommonYargsOptions,
9
+ YargsOptionsToInterface,
10
+ } from "../yargs-types";
11
+ import type { Argv } from "yargs";
12
+
13
+ export function generateOptions(yargs: Argv<CommonYargsOptions>) {
14
+ return yargs
15
+ .positional("name", {
16
+ describe: "Name of the Workers project",
17
+ type: "string",
18
+ demandOption: true,
19
+ })
20
+ .positional("template", {
21
+ type: "string",
22
+ describe: "The URL of a GitHub template",
23
+ })
24
+ .option("type", {
25
+ alias: "t",
26
+ type: "string",
27
+ hidden: true,
28
+ deprecated: true,
29
+ })
30
+ .option("site", {
31
+ alias: "s",
32
+ type: "boolean",
33
+ hidden: true,
34
+ deprecated: true,
35
+ });
36
+ }
37
+ type GenerateArgs = YargsOptionsToInterface<typeof generateOptions>;
38
+
39
+ // Originally, generate was a rust function: https://github.com/cloudflare/wrangler/blob/master/src/cli/mod.rs#L106-L123
40
+ export async function generateHandler(args: GenerateArgs) {
41
+ // somehow, `init` marks name as required but then also runs fine
42
+ // with the name omitted, and then substitutes it at runtime with ""
43
+ // delegate to `wrangler init` if no template is specified
44
+ if (args.template === undefined) {
45
+ return initHandler({
46
+ name: args.name,
47
+ template: undefined,
48
+ site: undefined,
49
+ yes: undefined,
50
+ fromDash: undefined,
51
+ "from-dash": undefined,
52
+ v: undefined,
53
+ config: undefined,
54
+ env: undefined,
55
+ type: undefined,
56
+ _: args._,
57
+ $0: args.$0,
58
+ });
59
+ }
60
+
61
+ // print down here cuz `init` prints it own its own
62
+ await printWranglerBanner();
63
+
64
+ if (args.type) {
65
+ let message = "The --type option is no longer supported.";
66
+ if (args.type === "webpack") {
67
+ message +=
68
+ "\nIf you wish to use webpack then you will need to create a custom build.";
69
+ // TODO: Add a link to docs
70
+ }
71
+ throw new CommandLineArgsError(message);
72
+ }
73
+
74
+ if (args.name && isRemote(args.name)) {
75
+ [args.template, args.name] = [args.name, args.template];
76
+ }
77
+
78
+ const creationDirectory = generateWorkerDirectoryName(args.name);
79
+
80
+ if (args.site) {
81
+ const gitDirectory =
82
+ creationDirectory !== process.cwd()
83
+ ? path.basename(creationDirectory)
84
+ : "my-site";
85
+ const message =
86
+ "The --site option is no longer supported.\n" +
87
+ "If you wish to create a brand new Worker Sites project then clone the `worker-sites-template` starter repository:\n\n" +
88
+ "```\n" +
89
+ `git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template ${gitDirectory}\n` +
90
+ `cd ${gitDirectory}\n` +
91
+ "```\n\n" +
92
+ "Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.\n" +
93
+ "Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/.";
94
+ throw new CommandLineArgsError(message);
95
+ }
96
+
97
+ logger.log(
98
+ `Creating a worker in ${path.basename(creationDirectory)} from ${
99
+ args.template
100
+ }`
101
+ );
102
+
103
+ const { remote, subdirectory } = parseTemplatePath(args.template);
104
+
105
+ await cloneIntoDirectory(remote, creationDirectory, subdirectory);
106
+ await initializeGit(creationDirectory);
107
+
108
+ logger.log("✨ Success!");
109
+ }
110
+
111
+ /**
112
+ * Creates a path based on the current working directory and a worker name.
113
+ * Automatically increments a counter when searching for an available directory.
114
+ *
115
+ * Running `wrangler generate worker https://some-git-repo` in a directory
116
+ * with the structure:
117
+ * ```
118
+ * - workers
119
+ * |
120
+ * | - worker
121
+ * | | - wrangler.toml
122
+ * | | ...
123
+ * |
124
+ * | - worker-1
125
+ * | | - wrangler.toml
126
+ * | | ...
127
+ * ```
128
+ *
129
+ * will result in a new worker called `worker-2` being generated.
130
+ *
131
+ * @param workerName the name of the generated worker
132
+ * @returns an absolute path to the directory to generate the worker into
133
+ */
134
+ function generateWorkerDirectoryName(workerName: string): string {
135
+ let workerDirectoryPath = path.resolve(process.cwd(), workerName);
136
+ let i = 1;
137
+
138
+ while (fs.existsSync(workerDirectoryPath)) {
139
+ workerDirectoryPath = path.resolve(process.cwd(), `${workerName}-${i}`);
140
+ i++;
141
+ }
142
+
143
+ return workerDirectoryPath;
144
+ }
145
+
146
+ /**
147
+ * Checks if an arg is a template, which can be useful if the order
148
+ * of template & worker name args is switched
149
+ *
150
+ * @param arg a template to generate from, or a folder to generate into
151
+ * @returns true if the given arg was remote (a template)
152
+ */
153
+ function isRemote(arg: string) {
154
+ return /^(https?|ftps?|file|git|ssh):\/\//.test(arg) || arg.includes(":");
155
+ }
156
+
157
+ /**
158
+ * unreadable regex basically copied from degit. i put some named capture groups in,
159
+ * but uhh...there's not much to do short of using pomsky or some other tool.
160
+ *
161
+ * notably: this only supports `https://` and `git@` urls,
162
+ * and is missing support for:
163
+ * - `http`
164
+ * - `ftp(s)`
165
+ * - `file`
166
+ * - `ssh`
167
+ */
168
+ const TEMPLATE_REGEX =
169
+ /^(?:(?:https:\/\/)?(?<httpsUrl>[^:/]+\.[^:/]+)\/|git@(?<gitUrl>[^:/]+)[:/]|(?<shorthandUrl>[^/]+):)?(?<user>[^/\s]+)\/(?<repository>[^/\s#]+)(?:(?<subdirectoryPath>(?:\/[^/\s#]+)+))?(?:\/)?(?:#(?<tag>.+))?/;
170
+
171
+ // there are a few URL formats we support:
172
+ // - `user/repo` -> assume github, use "https://github.com/user/repo.git"
173
+ // - `https://<httpsUrl>
174
+ // - `git@<gitUrl>`
175
+ // - `(bb|bitbucket|gh|github|gl|gitlab):user/repo` -> parse shorthand into https url
176
+
177
+ /**
178
+ * There's no URL, so assume a github repo
179
+ */
180
+ type NoUrl = {
181
+ httpsUrl: undefined;
182
+ gitUrl: undefined;
183
+ shorthandUrl: undefined;
184
+ };
185
+
186
+ /**
187
+ * A https url (e.g. https://bitbucket.org/user/repo)
188
+ */
189
+ type HttpsUrl = Omit<NoUrl, "httpsUrl"> & { httpsUrl: string };
190
+
191
+ /**
192
+ * A git url (e.g. git@gitlab.com:user/repo)
193
+ */
194
+ type GitUrl = Omit<NoUrl, "gitUrl"> & { gitUrl: string };
195
+
196
+ /**
197
+ * A shorthand url (e.g. github:user/repo)
198
+ */
199
+ type ShorthandUrl = Omit<NoUrl, "shorthandUrl"> & { shorthandUrl: string };
200
+
201
+ /**
202
+ * Union of all possible URL groups. Exactly one will be present, the rest
203
+ * will be `undefined`.
204
+ */
205
+ type TemplateRegexUrlGroup = NoUrl | HttpsUrl | GitUrl | ShorthandUrl;
206
+
207
+ /**
208
+ * Possible matches of `TEMPLATE_REGEX` against a passed-in template arg
209
+ */
210
+ type TemplateRegexGroups = {
211
+ /** The user the repo is under */
212
+ user: string;
213
+
214
+ /** The repo name */
215
+ repository: string;
216
+
217
+ /** Optional, path to subdirectory containing template. Begins with `/` */
218
+ subdirectoryPath?: string;
219
+
220
+ /** Optional tag (or branch, etc.) to clone */
221
+ tag?: string;
222
+ } & TemplateRegexUrlGroup;
223
+
224
+ /**
225
+ * Parses a regex match on any of the URL groups into a URL base
226
+ *
227
+ * @param urlGroup a regex hit for a URL of any sort
228
+ * @returns the protocol and domain name of the url to clone from
229
+ */
230
+ function toUrlBase({ httpsUrl, gitUrl, shorthandUrl }: TemplateRegexUrlGroup) {
231
+ if (httpsUrl !== undefined) {
232
+ return `https://${httpsUrl}`;
233
+ }
234
+
235
+ if (gitUrl !== undefined) {
236
+ return `git@${gitUrl}`;
237
+ }
238
+
239
+ if (shorthandUrl !== undefined) {
240
+ switch (shorthandUrl) {
241
+ case "github":
242
+ case "gh":
243
+ return "https://github.com";
244
+ case "gitlab":
245
+ case "gl":
246
+ return "https://gitlab.com";
247
+ case "bitbucket":
248
+ case "bb":
249
+ return "https://bitbucket.org";
250
+ default:
251
+ throw new Error(
252
+ `Unable to parse shorthand ${shorthandUrl}. Supported options are "bitbucket" ("bb"), "github" ("gh"), and "gitlab" ("gl")`
253
+ );
254
+ }
255
+ }
256
+
257
+ return "https://github.com";
258
+ }
259
+
260
+ /**
261
+ * Parses a template string (e.g. "user/repo", "github:user/repo/path/to/subdirectory")
262
+ * into a remote URL to clone from and an optional subdirectory to filter for
263
+ *
264
+ * @param templatePath the template string to parse
265
+ * @returns an object containing the remote url and an optional subdirectory to clone
266
+ */
267
+ function parseTemplatePath(templatePath: string): {
268
+ remote: string;
269
+ subdirectory?: string;
270
+ } {
271
+ if (!templatePath.includes("/")) {
272
+ // template is a cloudflare canonical template, it doesn't include a slash in the name
273
+ return {
274
+ remote: "https://github.com/cloudflare/templates.git",
275
+ subdirectory: templatePath,
276
+ };
277
+ }
278
+
279
+ const groups = TEMPLATE_REGEX.exec(templatePath)?.groups as unknown as
280
+ | TemplateRegexGroups
281
+ | undefined;
282
+
283
+ if (!groups) {
284
+ throw new Error(`Unable to parse ${templatePath} as a template`);
285
+ }
286
+
287
+ const { user, repository, subdirectoryPath, tag, ...urlGroups } = groups;
288
+
289
+ const urlBase = toUrlBase(urlGroups);
290
+ const isHttp = urlBase.startsWith("http");
291
+
292
+ const remote = `${urlBase}${isHttp ? "/" : ":"}${user}/${repository}.git${
293
+ tag ? `#${tag}` : ""
294
+ }`;
295
+
296
+ // remove starting /
297
+ const subdirectory = subdirectoryPath?.slice(1);
298
+
299
+ return { remote, subdirectory };
300
+ }
@@ -15,8 +15,6 @@ import {
15
15
  buildOptions,
16
16
  configHandler,
17
17
  noOpOptions,
18
- generateHandler,
19
- generateOptions,
20
18
  previewHandler,
21
19
  previewOptions,
22
20
  route,
@@ -26,6 +24,8 @@ import {
26
24
  } from "./deprecated";
27
25
  import { devHandler, devOptions } from "./dev";
28
26
  import { workerNamespaceCommands } from "./dispatch-namespace";
27
+ import { docsHandler, docsOptions } from "./docs";
28
+ import { generateHandler, generateOptions } from "./generate";
29
29
  import { initHandler, initOptions } from "./init";
30
30
  import { kvNamespace, kvKey, kvBulk } from "./kv";
31
31
  import { logBuildFailure, logger } from "./logger";
@@ -264,24 +264,30 @@ export function createCLIParser(argv: string[]) {
264
264
  // I wish we could enforce this pattern, but this comment will have to do for now.
265
265
  // (It's also annoying that choices[] doesn't get inferred as an enum. 🤷‍♂.)
266
266
 
267
- // [DEPRECATED] generate
267
+ // docs
268
268
  wrangler.command(
269
- // we definitely want to move away from us cloning github templates
270
- // we can do something better here, let's see
271
- "generate [name] [template]",
272
- false,
273
- generateOptions,
274
- generateHandler
269
+ "docs [command]",
270
+ "📚 Open wrangler's docs in your browser",
271
+ docsOptions,
272
+ docsHandler
275
273
  );
276
274
 
277
275
  // init
278
276
  wrangler.command(
279
277
  "init [name]",
280
- "📥 Create a wrangler.toml configuration file",
278
+ "📥 Initialize a basic Worker project, including a wrangler.toml file",
281
279
  initOptions,
282
280
  initHandler
283
281
  );
284
282
 
283
+ // generate
284
+ wrangler.command(
285
+ "generate [name] [template]",
286
+ "✨ Generate a new Worker project from an existing Worker template. See https://github.com/cloudflare/templates",
287
+ generateOptions,
288
+ generateHandler
289
+ );
290
+
285
291
  // [DEPRECATED] build
286
292
  wrangler.command("build", false, buildOptions, buildHandler);
287
293
 
package/src/init.ts CHANGED
@@ -22,10 +22,14 @@ import type { Route, SimpleRoute } from "./config/environment";
22
22
  import type { WorkerMetadata } from "./create-worker-upload-form";
23
23
  import type { ConfigPath } from "./index";
24
24
  import type { PackageManager } from "./package-manager";
25
- import type { CommonYargsOptions } from "./yargs-types";
26
- import type { Argv, ArgumentsCamelCase } from "yargs";
25
+ import type { PackageJSON } from "./parse";
26
+ import type {
27
+ CommonYargsOptions,
28
+ YargsOptionsToInterface,
29
+ } from "./yargs-types";
30
+ import type { Argv } from "yargs";
27
31
 
28
- export async function initOptions(yargs: Argv<CommonYargsOptions>) {
32
+ export function initOptions(yargs: Argv<CommonYargsOptions>) {
29
33
  return yargs
30
34
  .positional("name", {
31
35
  describe: "The name of your worker",
@@ -49,18 +53,14 @@ export async function initOptions(yargs: Argv<CommonYargsOptions>) {
49
53
  alias: "y",
50
54
  })
51
55
  .option("from-dash", {
52
- describe: "Download script from the dashboard for local development",
56
+ describe:
57
+ "The name of the Worker you wish to download from the Cloudflare dashboard for local development.",
53
58
  type: "string",
54
59
  requiresArg: true,
55
60
  });
56
61
  }
57
62
 
58
- interface InitArgs {
59
- name: string;
60
- type?: string;
61
- site?: boolean;
62
- yes?: boolean;
63
- }
63
+ type InitArgs = YargsOptionsToInterface<typeof initOptions>;
64
64
 
65
65
  export type ServiceMetadataRes = {
66
66
  id: string;
@@ -109,7 +109,7 @@ export type CronTriggersRes = {
109
109
  ];
110
110
  };
111
111
 
112
- export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
112
+ export async function initHandler(args: InitArgs) {
113
113
  await printWranglerBanner();
114
114
  if (args.type) {
115
115
  let message = "The --type option is no longer supported.";
@@ -124,7 +124,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
124
124
  const devDepsToInstall: string[] = [];
125
125
  const instructions: string[] = [];
126
126
  let shouldRunPackageManagerInstall = false;
127
- const fromDashScriptName = args["from-dash"] as string;
127
+ const fromDashScriptName = args.fromDash;
128
128
  const creationDirectory = path.resolve(
129
129
  process.cwd(),
130
130
  (args.name ? args.name : fromDashScriptName) ?? ""
@@ -165,6 +165,27 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
165
165
  );
166
166
  let justCreatedWranglerToml = false;
167
167
 
168
+ let accountId = "";
169
+ let serviceMetadata: undefined | ServiceMetadataRes;
170
+
171
+ // If --from-dash, check that script actually exists
172
+ if (fromDashScriptName) {
173
+ const config = readConfig(args.config as ConfigPath, args);
174
+ accountId = await requireAuth(config);
175
+ try {
176
+ serviceMetadata = await fetchResult<ServiceMetadataRes>(
177
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}`
178
+ );
179
+ } catch (err) {
180
+ if ((err as { code?: number }).code === 10090) {
181
+ throw new Error(
182
+ "wrangler couldn't find a Worker script with that name in your account.\nRun `wrangler whoami` to confirm you're logged into the correct account."
183
+ );
184
+ }
185
+ throw err;
186
+ }
187
+ }
188
+
168
189
  if (fs.existsSync(wranglerTomlDestination)) {
169
190
  let shouldContinue = false;
170
191
  logger.warn(
@@ -309,7 +330,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
309
330
  isTypescriptProject = true;
310
331
  await writeFile(
311
332
  path.join(creationDirectory, "./tsconfig.json"),
312
- readFileSync(path.join(getBasePath(), "templates/tsconfig.json"))
333
+ readFileSync(path.join(getBasePath(), "templates/tsconfig.init.json"))
313
334
  );
314
335
  devDepsToInstall.push("@cloudflare/workers-types");
315
336
  devDepsToInstall.push("typescript");
@@ -411,7 +432,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
411
432
  : `wrangler publish ${scriptPath}`,
412
433
  ...(isAddingTestScripts && { test: testRunner }),
413
434
  },
414
- },
435
+ } as PackageJSON,
415
436
  null,
416
437
  2
417
438
  ) + "\n"
@@ -449,19 +470,15 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
449
470
  );
450
471
  if (fromDashScriptName) {
451
472
  logger.warn(
452
- `After running "wrangler init --from-dash", modifying your worker via the Cloudflare dashboard is discouraged.
453
- Edits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish.`
473
+ "After running `wrangler init --from-dash`, modifying your worker via the Cloudflare dashboard is discouraged.\nEdits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish."
454
474
  );
455
- const config = readConfig(args.config as ConfigPath, args);
456
- const accountId = await requireAuth(config);
475
+
457
476
  await mkdir(path.join(creationDirectory, "./src"), {
458
477
  recursive: true,
459
478
  });
460
- const serviceMetaData = await fetchResult<ServiceMetadataRes>(
461
- `/accounts/${accountId}/workers/services/${fromDashScriptName}`
462
- );
479
+
463
480
  const defaultEnvironment =
464
- serviceMetaData.default_environment.environment;
481
+ serviceMetadata?.default_environment.environment;
465
482
  // I want the default environment, assuming it's the most up to date code.
466
483
  const dashScript = await fetchDashboardScript(
467
484
  `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/content`
@@ -479,7 +496,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
479
496
  scriptPath: "src/index.ts",
480
497
  extraToml: (await getWorkerConfig(accountId, fromDashScriptName, {
481
498
  defaultEnvironment,
482
- environments: serviceMetaData.environments,
499
+ environments: serviceMetadata?.environments,
483
500
  })) as TOML.JsonMap,
484
501
  });
485
502
  } else {
@@ -493,6 +510,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
493
510
  await mkdir(path.join(creationDirectory, "./src"), {
494
511
  recursive: true,
495
512
  });
513
+
496
514
  await writeFile(
497
515
  path.join(creationDirectory, "./src/index.ts"),
498
516
  readFileSync(path.join(getBasePath(), `templates/${template}`))
@@ -505,10 +523,43 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
505
523
  )}`
506
524
  );
507
525
 
526
+ shouldCreateTests =
527
+ yesFlag ||
528
+ (await confirm(
529
+ "Would you like us to write your first test with Vitest?"
530
+ ));
531
+
532
+ if (shouldCreateTests) {
533
+ if (yesFlag) {
534
+ logger.info("Your project will use Vitest to run your tests.");
535
+ }
536
+
537
+ newWorkerTestType = "vitest";
538
+ devDepsToInstall.push(newWorkerTestType);
539
+
540
+ await writeFile(
541
+ path.join(creationDirectory, "./src/index.test.ts"),
542
+ readFileSync(
543
+ path.join(
544
+ getBasePath(),
545
+ `templates/init-tests/test-${newWorkerTestType}-new-worker.ts`
546
+ )
547
+ )
548
+ );
549
+ logger.log(
550
+ `✨ Created ${path.relative(
551
+ process.cwd(),
552
+ path.join(creationDirectory, "./src/index.test.ts")
553
+ )}`
554
+ );
555
+ }
556
+
508
557
  await writePackageJsonScriptsAndUpdateWranglerToml({
509
558
  isWritingScripts: shouldWritePackageJsonScripts,
559
+ isAddingTests: shouldCreateTests,
510
560
  isCreatingWranglerToml: justCreatedWranglerToml,
511
561
  packagePath: pathToPackageJson,
562
+ testRunner: newWorkerTestType,
512
563
  scriptPath: "src/index.ts",
513
564
  extraToml: getNewWorkerToml(newWorkerType),
514
565
  });
@@ -524,20 +575,15 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
524
575
 
525
576
  if (fromDashScriptName) {
526
577
  logger.warn(
527
- `After running "wrangler init --from-dash", modifying your worker via the Cloudflare dashboard is discouraged.
528
- Edits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish.`
578
+ "After running `wrangler init --from-dash`, modifying your worker via the Cloudflare dashboard is discouraged.\nEdits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish."
529
579
  );
530
- const config = readConfig(args.config as ConfigPath, args);
531
- const accountId = await requireAuth(config);
580
+
532
581
  await mkdir(path.join(creationDirectory, "./src"), {
533
582
  recursive: true,
534
583
  });
535
584
 
536
- const serviceMetaData = await fetchResult<ServiceMetadataRes>(
537
- `/accounts/${accountId}/workers/services/${fromDashScriptName}`
538
- );
539
585
  const defaultEnvironment =
540
- serviceMetaData.default_environment.environment;
586
+ serviceMetadata?.default_environment.environment;
541
587
 
542
588
  // I want the default environment, assuming it's the most up to date code.
543
589
  const dashScript = await fetchDashboardScript(
@@ -557,7 +603,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
557
603
  //? Should we have Environment argument for `wrangler init --from-dash` - Jacob
558
604
  extraToml: (await getWorkerConfig(accountId, fromDashScriptName, {
559
605
  defaultEnvironment,
560
- environments: serviceMetaData.environments,
606
+ environments: serviceMetadata?.environments,
561
607
  })) as TOML.JsonMap,
562
608
  });
563
609
  } else {
@@ -588,7 +634,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
588
634
  (await confirm("Would you like us to write your first test?"));
589
635
 
590
636
  if (shouldCreateTests) {
591
- newWorkerTestType = await getNewWorkerTestType();
637
+ newWorkerTestType = await getNewWorkerTestType(yesFlag);
592
638
  devDepsToInstall.push(newWorkerTestType);
593
639
  await writeFile(
594
640
  path.join(creationDirectory, "./src/index.test.js"),
@@ -678,41 +724,41 @@ async function installPackages(
678
724
  }
679
725
 
680
726
  async function getNewWorkerType(newWorkerFilename: string) {
681
- return select(
682
- `Would you like to create a Worker at ${newWorkerFilename}?`,
683
- [
727
+ return select(`Would you like to create a Worker at ${newWorkerFilename}?`, {
728
+ choices: [
684
729
  {
685
730
  value: "none",
686
- label: "None",
731
+ title: "None",
687
732
  },
688
733
  {
689
734
  value: "fetch",
690
- label: "Fetch handler",
735
+ title: "Fetch handler",
691
736
  },
692
737
  {
693
738
  value: "scheduled",
694
- label: "Scheduled handler",
739
+ title: "Scheduled handler",
695
740
  },
696
741
  ],
697
- 1
698
- ) as Promise<"none" | "fetch" | "scheduled">;
742
+ defaultOption: 1,
743
+ });
699
744
  }
700
745
 
701
- async function getNewWorkerTestType() {
702
- return select(
703
- `Which test runner would you like to use?`,
704
- [
705
- {
706
- value: "vitest",
707
- label: "Vitest",
708
- },
709
- {
710
- value: "jest",
711
- label: "Jest",
712
- },
713
- ],
714
- 1
715
- ) as Promise<"jest" | "vitest">;
746
+ async function getNewWorkerTestType(yesFlag?: boolean) {
747
+ return yesFlag
748
+ ? "jest"
749
+ : select(`Which test runner would you like to use?`, {
750
+ choices: [
751
+ {
752
+ value: "vitest",
753
+ title: "Vitest",
754
+ },
755
+ {
756
+ value: "jest",
757
+ title: "Jest",
758
+ },
759
+ ],
760
+ defaultOption: 1,
761
+ });
716
762
  }
717
763
 
718
764
  function getNewWorkerTemplate(
@@ -770,8 +816,8 @@ async function getWorkerConfig(
770
816
  defaultEnvironment,
771
817
  environments,
772
818
  }: {
773
- defaultEnvironment: string;
774
- environments: ServiceMetadataRes["environments"];
819
+ defaultEnvironment: string | undefined;
820
+ environments: ServiceMetadataRes["environments"] | undefined;
775
821
  }
776
822
  ): Promise<RawConfig> {
777
823
  const [bindings, routes, serviceEnvMetadata, cronTriggers] =
@@ -870,7 +916,7 @@ async function getWorkerConfig(
870
916
  ];
871
917
  }
872
918
  break;
873
- case "namespace":
919
+ case "dispatch_namespace":
874
920
  {
875
921
  configObj.dispatch_namespaces = [
876
922
  ...(configObj.dispatch_namespaces ?? []),
@@ -967,7 +1013,7 @@ async function getWorkerConfig(
967
1013
  crons: cronTriggers.schedules.map((scheduled) => scheduled.cron),
968
1014
  },
969
1015
  env: environments
970
- .filter((env) => env.environment !== "production")
1016
+ ?.filter((env) => env.environment !== "production")
971
1017
  // `env` can have multiple Environments, with different configs.
972
1018
  .reduce((envObj, { environment }) => {
973
1019
  return { ...envObj, [environment]: {} };
package/src/jest.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare global {
2
+ const jest: unknown;
3
+ }
4
+ export {};