wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9

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 (239) hide show
  1. package/README.md +16 -2
  2. package/bin/wrangler.js +31 -11
  3. package/miniflare-dist/index.mjs +1340 -330
  4. package/package.json +40 -13
  5. package/src/__tests__/access.test.ts +25 -0
  6. package/src/__tests__/api-dev.test.ts +238 -0
  7. package/src/__tests__/api-devregistry.test.js +64 -0
  8. package/src/__tests__/api.test.ts +80 -0
  9. package/src/__tests__/configuration.test.ts +683 -470
  10. package/src/__tests__/d1.test.ts +123 -0
  11. package/src/__tests__/delete.test.ts +263 -0
  12. package/src/__tests__/deployments.test.ts +105 -0
  13. package/src/__tests__/dev.test.tsx +453 -70
  14. package/src/__tests__/dialogs.test.tsx +40 -0
  15. package/src/__tests__/generate.test.ts +235 -0
  16. package/src/__tests__/get-host-from-url.test.ts +16 -0
  17. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  18. package/src/__tests__/helpers/mock-cfetch.ts +92 -26
  19. package/src/__tests__/helpers/mock-console.ts +11 -2
  20. package/src/__tests__/helpers/mock-dialogs.ts +1 -1
  21. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +1 -1
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
  25. package/src/__tests__/helpers/mock-process.ts +8 -13
  26. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  27. package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
  28. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  29. package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
  30. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  31. package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
  32. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  33. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  34. package/src/__tests__/helpers/msw/index.ts +21 -0
  35. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  36. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  37. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  38. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  39. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  40. package/src/__tests__/index.test.ts +103 -97
  41. package/src/__tests__/init.test.ts +1484 -382
  42. package/src/__tests__/jest.setup.ts +88 -5
  43. package/src/__tests__/kv.test.ts +655 -540
  44. package/src/__tests__/logout.test.ts +47 -0
  45. package/src/__tests__/metrics.test.ts +95 -45
  46. package/src/__tests__/middleware.scheduled.test.ts +145 -0
  47. package/src/__tests__/middleware.test.ts +750 -0
  48. package/src/__tests__/pages-deployment-tail.test.ts +884 -0
  49. package/src/__tests__/pages.test.ts +1301 -119
  50. package/src/__tests__/paths.test.ts +39 -0
  51. package/src/__tests__/publish.test.ts +2266 -1180
  52. package/src/__tests__/pubsub.test.ts +211 -88
  53. package/src/__tests__/queues.test.ts +522 -0
  54. package/src/__tests__/r2.test.ts +363 -203
  55. package/src/__tests__/secret.test.ts +261 -59
  56. package/src/__tests__/tail.test.ts +352 -151
  57. package/src/__tests__/test-old-node-version.js +3 -3
  58. package/src/__tests__/tsconfig-sanity.ts +12 -0
  59. package/src/__tests__/tsconfig.json +8 -0
  60. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  61. package/src/__tests__/type-generation.test.ts +233 -0
  62. package/src/__tests__/user.test.ts +59 -73
  63. package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
  64. package/src/__tests__/validate-dev-props.test.ts +56 -0
  65. package/src/__tests__/whoami.test.tsx +114 -66
  66. package/src/__tests__/worker-namespace.test.ts +154 -155
  67. package/src/api/dev.ts +170 -38
  68. package/src/api/index.ts +1 -0
  69. package/src/bundle-reporter.tsx +41 -2
  70. package/src/bundle.ts +668 -81
  71. package/src/cfetch/index.ts +40 -3
  72. package/src/cfetch/internal.ts +144 -32
  73. package/src/cli.ts +4 -3
  74. package/src/config/config.ts +27 -3
  75. package/src/config/environment.ts +131 -7
  76. package/src/config/index.ts +128 -8
  77. package/src/config/validation-helpers.ts +19 -6
  78. package/src/config/validation.ts +411 -14
  79. package/src/config-cache.ts +3 -2
  80. package/src/create-worker-preview.ts +31 -6
  81. package/src/create-worker-upload-form.ts +84 -22
  82. package/src/d1/backups.tsx +223 -0
  83. package/src/d1/constants.ts +2 -0
  84. package/src/d1/create.tsx +63 -0
  85. package/src/d1/delete.tsx +56 -0
  86. package/src/d1/execute.tsx +352 -0
  87. package/src/d1/formatTimeAgo.ts +14 -0
  88. package/src/d1/index.ts +101 -0
  89. package/src/d1/list.tsx +48 -0
  90. package/src/d1/migrations/apply.tsx +196 -0
  91. package/src/d1/migrations/create.tsx +77 -0
  92. package/src/d1/migrations/helpers.ts +147 -0
  93. package/src/d1/migrations/index.tsx +3 -0
  94. package/src/d1/migrations/list.tsx +79 -0
  95. package/src/d1/migrations/options.ts +18 -0
  96. package/src/d1/options.ts +22 -0
  97. package/src/d1/types.tsx +24 -0
  98. package/src/d1/utils.ts +48 -0
  99. package/src/delete.ts +108 -0
  100. package/src/deployments.ts +83 -0
  101. package/src/deprecated/index.ts +145 -0
  102. package/src/dev/dev-vars.ts +11 -8
  103. package/src/dev/dev.tsx +220 -58
  104. package/src/dev/get-local-persistence-path.tsx +31 -0
  105. package/src/dev/local.tsx +740 -181
  106. package/src/dev/remote.tsx +340 -123
  107. package/src/dev/start-server.ts +543 -0
  108. package/src/dev/use-esbuild.ts +61 -7
  109. package/src/dev/validate-dev-props.ts +31 -0
  110. package/src/dev-registry.tsx +201 -0
  111. package/src/dev.tsx +614 -258
  112. package/src/dialogs.tsx +41 -8
  113. package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
  114. package/src/docs/index.ts +97 -0
  115. package/src/entry.ts +3 -2
  116. package/src/environment-variables/factory.ts +88 -0
  117. package/src/environment-variables/misc-variables.ts +30 -0
  118. package/src/generate/index.ts +300 -0
  119. package/src/git-client.ts +100 -7
  120. package/src/index.tsx +249 -1560
  121. package/src/init.ts +626 -150
  122. package/src/inspect.ts +137 -32
  123. package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
  124. package/src/is-interactive.ts +4 -0
  125. package/src/jest.d.ts +4 -0
  126. package/src/{kv.ts → kv/helpers.ts} +3 -3
  127. package/src/kv/index.ts +596 -0
  128. package/src/logger.ts +27 -8
  129. package/src/metrics/index.ts +1 -0
  130. package/src/metrics/metrics-config.ts +4 -4
  131. package/src/metrics/metrics-dispatcher.ts +1 -0
  132. package/src/metrics/metrics-usage-headers.ts +24 -0
  133. package/src/metrics/send-event.ts +13 -8
  134. package/src/miniflare-cli/assets.ts +172 -465
  135. package/src/miniflare-cli/index.ts +149 -24
  136. package/src/miniflare-cli/tsconfig.json +9 -0
  137. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  138. package/src/miniflare-cli/types.ts +11 -0
  139. package/src/module-collection.ts +12 -3
  140. package/src/pages/build.tsx +108 -42
  141. package/src/pages/constants.ts +11 -0
  142. package/src/pages/deployment-tails.tsx +284 -0
  143. package/src/pages/deployments.tsx +9 -31
  144. package/src/pages/dev.tsx +417 -108
  145. package/src/pages/errors.ts +66 -0
  146. package/src/pages/functions/buildPlugin.ts +86 -84
  147. package/src/pages/functions/buildWorker.ts +121 -106
  148. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  149. package/src/pages/functions/routes-consolidation.ts +73 -0
  150. package/src/pages/functions/routes-transformation.test.ts +282 -0
  151. package/src/pages/functions/routes-transformation.ts +115 -0
  152. package/src/pages/functions/routes-validation.test.ts +403 -0
  153. package/src/pages/functions/routes-validation.ts +202 -0
  154. package/src/pages/functions/tsconfig.json +8 -0
  155. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  156. package/src/pages/functions.tsx +82 -0
  157. package/src/pages/hash.tsx +13 -0
  158. package/src/pages/index.tsx +73 -55
  159. package/src/pages/projects.tsx +9 -3
  160. package/src/pages/prompt-select-project.tsx +31 -0
  161. package/src/pages/publish.tsx +183 -60
  162. package/src/pages/types.ts +1 -0
  163. package/src/pages/upload.tsx +107 -56
  164. package/src/pages/utils.ts +13 -2
  165. package/src/paths.ts +49 -4
  166. package/src/proxy.ts +375 -157
  167. package/src/publish/index.ts +270 -0
  168. package/src/{publish.ts → publish/publish.ts} +431 -188
  169. package/src/pubsub/pubsub-commands.tsx +4 -3
  170. package/src/queues/cli/commands/consumer/add.ts +71 -0
  171. package/src/queues/cli/commands/consumer/index.ts +22 -0
  172. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  173. package/src/queues/cli/commands/create.ts +25 -0
  174. package/src/queues/cli/commands/delete.ts +26 -0
  175. package/src/queues/cli/commands/index.ts +36 -0
  176. package/src/queues/cli/commands/list.ts +25 -0
  177. package/src/queues/client.ts +135 -0
  178. package/src/queues/utils.ts +18 -0
  179. package/src/r2/constants.ts +4 -0
  180. package/src/r2/helpers.ts +131 -0
  181. package/src/r2/index.ts +289 -0
  182. package/src/routes.ts +140 -0
  183. package/src/secret/index.ts +379 -0
  184. package/src/sites.tsx +3 -3
  185. package/src/tail/createTail.ts +379 -0
  186. package/src/tail/filters.ts +4 -6
  187. package/src/tail/index.ts +199 -284
  188. package/src/tail/printing.ts +43 -3
  189. package/src/tsconfig-sanity.ts +16 -0
  190. package/src/type-generation.ts +181 -0
  191. package/src/user/access.ts +68 -0
  192. package/src/user/auth-variables.ts +113 -0
  193. package/src/user/choose-account.tsx +1 -1
  194. package/src/user/index.ts +0 -1
  195. package/src/user/user.tsx +116 -49
  196. package/src/utils/collectKeyValues.ts +14 -0
  197. package/src/whoami.tsx +80 -2
  198. package/src/worker.ts +69 -9
  199. package/src/yargs-types.ts +24 -0
  200. package/src/zones.ts +124 -9
  201. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  202. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  203. package/templates/__tests__/tsconfig.json +8 -0
  204. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  205. package/templates/d1-beta-facade.js +205 -0
  206. package/templates/facade.d.ts +14 -0
  207. package/templates/first-party-worker-module-facade.ts +19 -0
  208. package/templates/format-dev-errors.ts +33 -0
  209. package/templates/init-tests/test-jest-new-worker.js +23 -0
  210. package/templates/init-tests/test-vitest-new-worker.js +24 -0
  211. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  212. package/templates/middleware/common.ts +62 -0
  213. package/templates/middleware/loader-modules.ts +82 -0
  214. package/templates/middleware/loader-sw.ts +233 -0
  215. package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
  216. package/templates/middleware/middleware-pretty-error.ts +40 -0
  217. package/templates/middleware/middleware-scheduled.ts +15 -0
  218. package/templates/new-worker.js +1 -1
  219. package/templates/pages-dev-pipeline.ts +31 -0
  220. package/templates/pages-dev-util.ts +52 -0
  221. package/templates/pages-shim.ts +4 -1
  222. package/templates/pages-template-plugin.ts +21 -5
  223. package/templates/pages-template-worker.ts +37 -9
  224. package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
  225. package/templates/service-bindings-module-facade.js +54 -0
  226. package/templates/service-bindings-sw-facade.js +42 -0
  227. package/templates/tsconfig-sanity.ts +11 -0
  228. package/templates/tsconfig.init.json +106 -0
  229. package/templates/tsconfig.json +5 -102
  230. package/templates/tsconfig.tsbuildinfo +1 -0
  231. package/wrangler-dist/cli.d.ts +621 -42
  232. package/wrangler-dist/cli.js +144441 -102606
  233. package/src/environment-variables.ts +0 -35
  234. package/src/faye-websocket.d.ts +0 -6
  235. package/src/generate.ts +0 -33
  236. package/src/miniflare-cli/enum-keys.ts +0 -17
  237. package/src/preview.ts +0 -31
  238. package/src/r2.ts +0 -50
  239. package/src/user/env-vars.ts +0 -46
package/src/pages/dev.tsx CHANGED
@@ -1,33 +1,32 @@
1
1
  import { execSync, spawn } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { tmpdir } from "node:os";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { homedir, tmpdir } from "node:os";
4
4
  import { join, resolve } from "node:path";
5
5
  import { watch } from "chokidar";
6
+ import * as esbuild from "esbuild";
6
7
  import { unstable_dev } from "../api";
8
+ import { esbuildAliasExternalPlugin } from "../bundle";
7
9
  import { FatalError } from "../errors";
8
10
  import { logger } from "../logger";
9
11
  import * as metrics from "../metrics";
12
+ import { getBasePath } from "../paths";
10
13
  import { buildFunctions } from "./build";
11
- import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
14
+ import { ROUTES_SPEC_VERSION, SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
15
+ import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
16
+ import { validateRoutes } from "./functions/routes-validation";
12
17
  import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils";
13
- import type { ArgumentsCamelCase, Argv } from "yargs";
14
-
15
- type PagesDevArgs = {
16
- directory?: string;
17
- command?: string;
18
- local: boolean;
19
- port: number;
20
- proxy?: number;
21
- "script-path": string;
22
- binding?: (string | number)[];
23
- kv?: (string | number)[];
24
- do?: (string | number)[];
25
- "live-reload": boolean;
26
- "local-protocol"?: "https" | "http";
27
- "node-compat": boolean;
28
- };
18
+ import type { AdditionalDevProps } from "../dev";
19
+ import type { YargsOptionsToInterface } from "../yargs-types";
20
+ import type { RoutesJSONSpec } from "./functions/routes-transformation";
21
+ import type { Argv } from "yargs";
22
+
23
+ const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
24
+ /^(?<binding>[^=]+)=(?<className>[^@\s]+)(@(?<scriptName>.*)$)?$/
25
+ );
29
26
 
30
- export function Options(yargs: Argv): Argv<PagesDevArgs> {
27
+ type PagesDevArgs = YargsOptionsToInterface<typeof Options>;
28
+
29
+ export function Options(yargs: Argv) {
31
30
  return yargs
32
31
  .positional("directory", {
33
32
  type: "string",
@@ -45,11 +44,30 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
45
44
  default: true,
46
45
  description: "Run on my machine",
47
46
  },
47
+ "compatibility-date": {
48
+ describe: "Date to use for compatibility checks",
49
+ type: "string",
50
+ },
51
+ "compatibility-flags": {
52
+ describe: "Flags to use for compatibility checks",
53
+ alias: "compatibility-flag",
54
+ type: "string",
55
+ array: true,
56
+ },
57
+ ip: {
58
+ type: "string",
59
+ default: "0.0.0.0",
60
+ description: "The IP address to listen on",
61
+ },
48
62
  port: {
49
63
  type: "number",
50
64
  default: 8788,
51
65
  description: "The port to listen on (serve from)",
52
66
  },
67
+ "inspector-port": {
68
+ type: "number",
69
+ describe: "Port for devtools to connect to",
70
+ },
53
71
  proxy: {
54
72
  type: "number",
55
73
  description: "The port to proxy (where the static assets are served)",
@@ -67,14 +85,22 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
67
85
  },
68
86
  kv: {
69
87
  type: "array",
70
- description: "KV namespace to bind",
88
+ description: "KV namespace to bind (--kv KV_BINDING)",
71
89
  alias: "k",
72
90
  },
91
+ d1: {
92
+ type: "array",
93
+ description: "D1 database to bind",
94
+ },
73
95
  do: {
74
96
  type: "array",
75
- description: "Durable Object to bind (NAME=CLASS)",
97
+ description: "Durable Object to bind (--do NAME=CLASS)",
76
98
  alias: "o",
77
99
  },
100
+ r2: {
101
+ type: "array",
102
+ description: "R2 bucket to bind (--r2 R2_BINDING)",
103
+ },
78
104
  "live-reload": {
79
105
  type: "boolean",
80
106
  default: false,
@@ -84,17 +110,44 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
84
110
  describe: "Protocol to listen to requests on, defaults to http.",
85
111
  choices: ["http", "https"] as const,
86
112
  },
113
+ "experimental-enable-local-persistence": {
114
+ describe:
115
+ "Enable persistence for local mode (deprecated, use --persist)",
116
+ type: "boolean",
117
+ deprecated: true,
118
+ hidden: true,
119
+ },
120
+ persist: {
121
+ describe:
122
+ "Enable persistence for local mode, using default path: .wrangler/state",
123
+ type: "boolean",
124
+ },
125
+ "persist-to": {
126
+ describe:
127
+ "Specify directory to use for local persistence (implies --persist)",
128
+ type: "string",
129
+ requiresArg: true,
130
+ },
87
131
  "node-compat": {
88
132
  describe: "Enable node.js compatibility",
89
133
  default: false,
90
134
  type: "boolean",
91
135
  hidden: true,
92
136
  },
137
+ "experimental-local": {
138
+ describe: "Run on my machine using the Cloudflare Workers runtime",
139
+ type: "boolean",
140
+ default: false,
141
+ },
93
142
  config: {
94
143
  describe: "Pages does not support wrangler.toml",
95
144
  type: "string",
96
145
  hidden: true,
97
146
  },
147
+ "log-level": {
148
+ choices: ["debug", "info", "log", "warn", "error", "none"] as const,
149
+ describe: "Specify logging level",
150
+ },
98
151
  })
99
152
  .epilogue(pagesBetaWarning);
100
153
  }
@@ -102,21 +155,38 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
102
155
  export const Handler = async ({
103
156
  local,
104
157
  directory,
158
+ "compatibility-date": compatibilityDate,
159
+ "compatibility-flags": compatibilityFlags,
160
+ ip,
105
161
  port,
162
+ "inspector-port": inspectorPort,
106
163
  proxy: requestedProxyPort,
107
164
  "script-path": singleWorkerScriptPath,
108
165
  binding: bindings = [],
109
166
  kv: kvs = [],
110
167
  do: durableObjects = [],
168
+ d1: d1s = [],
169
+ r2: r2s = [],
111
170
  "live-reload": liveReload,
112
171
  "local-protocol": localProtocol,
172
+ experimentalEnableLocalPersistence,
173
+ persist,
174
+ persistTo,
113
175
  "node-compat": nodeCompat,
176
+ "experimental-local": experimentalLocal,
114
177
  config: config,
115
178
  _: [_pages, _dev, ...remaining],
116
- }: ArgumentsCamelCase<PagesDevArgs>) => {
179
+ logLevel,
180
+ }: PagesDevArgs) => {
117
181
  // Beta message for `wrangler pages <commands>` usage
118
182
  logger.log(pagesBetaWarning);
119
183
 
184
+ type LogLevelArg = "debug" | "info" | "log" | "warn" | "error" | "none";
185
+ if (logLevel) {
186
+ // The YargsOptionsToInterface doesn't handle the passing in of Unions from choices in Yargs
187
+ logger.loggerLevel = logLevel as LogLevelArg;
188
+ }
189
+
120
190
  if (!local) {
121
191
  throw new FatalError("Only local mode is supported at the moment.", 1);
122
192
  }
@@ -125,14 +195,16 @@ export const Handler = async ({
125
195
  throw new FatalError("Pages does not support wrangler.toml", 1);
126
196
  }
127
197
 
128
- const functionsDirectory = "./functions";
129
- const usingFunctions = existsSync(functionsDirectory);
130
-
131
198
  const command = remaining;
132
199
 
133
200
  let proxyPort: number | undefined;
134
201
 
135
- if (directory === undefined) {
202
+ if (directory !== undefined && command.length > 0) {
203
+ throw new FatalError(
204
+ "Specify either a directory OR a proxy command, not both.",
205
+ 1
206
+ );
207
+ } else if (directory === undefined) {
136
208
  proxyPort = await spawnProxyProcess({
137
209
  port: requestedProxyPort,
138
210
  command,
@@ -142,15 +214,71 @@ export const Handler = async ({
142
214
  directory = resolve(directory);
143
215
  }
144
216
 
217
+ if (!compatibilityDate) {
218
+ const currentDate = new Date().toISOString().substring(0, 10);
219
+ logger.warn(
220
+ `No compatibility_date was specified. Using today's date: ${currentDate}.\n` +
221
+ "Pass it in your terminal:\n" +
222
+ "```\n" +
223
+ `--compatibility-date=${currentDate}\n` +
224
+ "```\n" +
225
+ "See https://developers.cloudflare.com/workers/platform/compatibility-dates/ for more information."
226
+ );
227
+ compatibilityDate = currentDate;
228
+ }
229
+
230
+ if (experimentalEnableLocalPersistence) {
231
+ logger.warn(
232
+ `--experimental-enable-local-persistence is deprecated.\n` +
233
+ `Move any existing data to .wrangler/state and use --persist, or\n` +
234
+ `use --persist-to=./wrangler-local-state to keep using the old path.`
235
+ );
236
+ }
237
+
145
238
  let scriptReadyResolve: () => void;
146
239
  const scriptReadyPromise = new Promise<void>(
147
240
  (promiseResolve) => (scriptReadyResolve = promiseResolve)
148
241
  );
149
242
 
150
- let scriptPath: string;
243
+ const workerScriptPath =
244
+ directory !== undefined
245
+ ? join(directory, singleWorkerScriptPath)
246
+ : singleWorkerScriptPath;
247
+ const usingWorkerScript = existsSync(workerScriptPath);
248
+
249
+ const functionsDirectory = "./functions";
250
+ let usingFunctions = !usingWorkerScript && existsSync(functionsDirectory);
251
+
252
+ let scriptPath = "";
253
+
254
+ if (usingWorkerScript) {
255
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
256
+ scriptReadyResolve!();
151
257
 
152
- if (usingFunctions) {
153
- const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
258
+ scriptPath = workerScriptPath;
259
+
260
+ const runBuild = async () => {
261
+ try {
262
+ await esbuild.build({
263
+ entryPoints: [scriptPath],
264
+ write: false,
265
+ // we need it to be bundled so that any imports that are used are affected by the blocker plugin
266
+ bundle: true,
267
+ plugins: [blockWorkerJsImports],
268
+ });
269
+ } catch {}
270
+ };
271
+
272
+ await runBuild();
273
+ watch([scriptPath], {
274
+ persistent: true,
275
+ ignoreInitial: true,
276
+ }).on("all", async () => {
277
+ await runBuild();
278
+ });
279
+ } else if (usingFunctions) {
280
+ // Try to use Functions
281
+ const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.mjs`);
154
282
  scriptPath = outfile;
155
283
 
156
284
  if (nodeCompat) {
@@ -160,111 +288,264 @@ export const Handler = async ({
160
288
  }
161
289
 
162
290
  logger.log(`Compiling worker to "${outfile}"...`);
163
-
291
+ const onEnd = () => scriptReadyResolve();
164
292
  try {
165
293
  await buildFunctions({
166
294
  outfile,
167
295
  functionsDirectory,
168
296
  sourcemap: true,
169
297
  watch: true,
170
- onEnd: () => scriptReadyResolve(),
298
+ onEnd,
171
299
  buildOutputDirectory: directory,
172
300
  nodeCompat,
301
+ local: true,
173
302
  });
174
303
  await metrics.sendMetricsEvent("build pages functions");
175
- } catch {}
176
304
 
177
- watch([functionsDirectory], {
178
- persistent: true,
179
- ignoreInitial: true,
180
- }).on("all", async () => {
181
- await buildFunctions({
182
- outfile,
183
- functionsDirectory,
184
- sourcemap: true,
185
- watch: true,
186
- onEnd: () => scriptReadyResolve(),
187
- buildOutputDirectory: directory,
188
- nodeCompat,
305
+ // If Functions found routes, continue using Functions
306
+ watch([functionsDirectory], {
307
+ persistent: true,
308
+ ignoreInitial: true,
309
+ }).on("all", async () => {
310
+ try {
311
+ await buildFunctions({
312
+ outfile,
313
+ functionsDirectory,
314
+ sourcemap: true,
315
+ watch: true,
316
+ onEnd,
317
+ buildOutputDirectory: directory,
318
+ nodeCompat,
319
+ local: true,
320
+ });
321
+ await metrics.sendMetricsEvent("build pages functions");
322
+ } catch (e) {
323
+ if (e instanceof FunctionsNoRoutesError) {
324
+ logger.warn(
325
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
326
+ );
327
+ } else {
328
+ throw e;
329
+ }
330
+ }
189
331
  });
190
- await metrics.sendMetricsEvent("build pages functions");
191
- });
192
- } else {
332
+ } catch (e) {
333
+ // If there are no Functions, then Pages will only serve assets.
334
+ if (e instanceof FunctionsNoRoutesError) {
335
+ logger.warn(
336
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
337
+ );
338
+ // Resolve anyway and run without Functions
339
+ onEnd();
340
+ // Turn off Functions
341
+ usingFunctions = false;
342
+ } else {
343
+ throw e;
344
+ }
345
+ }
346
+ }
347
+
348
+ // Depending on the result of building Functions, we may not actually be using
349
+ // Functions even if the directory exists.
350
+ if (!usingFunctions && !usingWorkerScript) {
193
351
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
194
352
  scriptReadyResolve!();
195
353
 
196
- scriptPath =
197
- directory !== undefined
198
- ? join(directory, singleWorkerScriptPath)
199
- : singleWorkerScriptPath;
354
+ logger.log("No functions. Shimming...");
355
+ scriptPath = resolve(getBasePath(), "templates/pages-shim.ts");
356
+ }
357
+
358
+ await scriptReadyPromise;
359
+
360
+ if (scriptPath === "") {
361
+ // Failed to get a script with or without Functions,
362
+ // something really bad must have happend.
363
+ throw new FatalError(
364
+ "Failed to start wrangler pages dev due to an unknown error",
365
+ 1
366
+ );
367
+ }
200
368
 
201
- if (!existsSync(scriptPath)) {
202
- logger.log("No functions. Shimming...");
203
- scriptPath = resolve(__dirname, "../templates/pages-shim.ts");
369
+ let entrypoint = scriptPath;
370
+
371
+ // custom _routes.json apply only to Functions or Advanced Mode Pages projects
372
+ if (directory && (usingFunctions || usingWorkerScript)) {
373
+ const routesJSONPath = join(directory, "_routes.json");
374
+
375
+ if (existsSync(routesJSONPath)) {
376
+ let routesJSONContents: string;
377
+ const runBuild = async (
378
+ entrypointFile: string,
379
+ outfile: string,
380
+ routes: string
381
+ ) => {
382
+ await esbuild.build({
383
+ entryPoints: [
384
+ resolve(getBasePath(), "templates/pages-dev-pipeline.ts"),
385
+ ],
386
+ bundle: true,
387
+ sourcemap: true,
388
+ format: "esm",
389
+ plugins: [
390
+ esbuildAliasExternalPlugin({
391
+ __ENTRY_POINT__: entrypointFile,
392
+ "./pages-dev-util": resolve(
393
+ getBasePath(),
394
+ "templates/pages-dev-util.ts"
395
+ ),
396
+ }),
397
+ ],
398
+ outfile,
399
+ define: {
400
+ __ROUTES__: routes,
401
+ },
402
+ });
403
+ };
404
+
405
+ try {
406
+ // always run the routes validation first. If _routes.json is invalid we
407
+ // want to throw accordingly and exit.
408
+ routesJSONContents = readFileSync(routesJSONPath, "utf-8");
409
+ validateRoutes(JSON.parse(routesJSONContents), directory);
410
+
411
+ entrypoint = join(
412
+ tmpdir(),
413
+ `${Math.random().toString(36).slice(2)}.js`
414
+ );
415
+ await runBuild(scriptPath, entrypoint, routesJSONContents);
416
+ } catch (err) {
417
+ if (err instanceof FatalError) {
418
+ throw err;
419
+ } else {
420
+ throw new FatalError(
421
+ `Could not validate _routes.json at ${directory}: ${err}`,
422
+ 1
423
+ );
424
+ }
425
+ }
426
+
427
+ watch([routesJSONPath], {
428
+ persistent: true,
429
+ ignoreInitial: true,
430
+ }).on("all", async () => {
431
+ try {
432
+ /**
433
+ * Watch for _routes.json file changes and validate file each time.
434
+ * If file is valid proceed to running the build.
435
+ */
436
+ routesJSONContents = readFileSync(routesJSONPath, "utf-8");
437
+ validateRoutes(JSON.parse(routesJSONContents), directory as string);
438
+ await runBuild(scriptPath, entrypoint, routesJSONContents);
439
+ } catch (err) {
440
+ /**
441
+ * If _routes.json is invalid, don't exit but instead fallback to a sensible default
442
+ * and continue to serve the assets. At the same time make sure we warn users that we
443
+ * we detected an invalid file and that we'll be using a default.
444
+ * This basically equivalates to serving a Functions or _worker.js project as is,
445
+ * without applying any additional routing rules on top.
446
+ */
447
+ const error =
448
+ err instanceof FatalError
449
+ ? err
450
+ : `Could not validate _routes.json at ${directory}: ${err}`;
451
+ const defaultRoutesJSONSpec: RoutesJSONSpec = {
452
+ version: ROUTES_SPEC_VERSION,
453
+ include: ["/*"],
454
+ exclude: [],
455
+ };
456
+
457
+ logger.error(error);
458
+ logger.warn(
459
+ `Falling back to the following _routes.json default: ${JSON.stringify(
460
+ defaultRoutesJSONSpec,
461
+ null,
462
+ 2
463
+ )}`
464
+ );
465
+
466
+ routesJSONContents = JSON.stringify(defaultRoutesJSONSpec);
467
+ await runBuild(scriptPath, entrypoint, routesJSONContents);
468
+ }
469
+ });
204
470
  }
205
471
  }
206
472
 
207
- await scriptReadyPromise;
473
+ const { stop, waitUntilExit } = await unstable_dev(entrypoint, {
474
+ ip,
475
+ port,
476
+ inspectorPort,
477
+ watch: true,
478
+ localProtocol,
479
+ liveReload,
480
+ compatibilityDate,
481
+ compatibilityFlags,
482
+ nodeCompat,
483
+ experimentalLocal,
484
+ vars: Object.fromEntries(
485
+ bindings
486
+ .map((binding) => binding.toString().split("="))
487
+ .map(([key, ...values]) => [key, values.join("=")])
488
+ ),
489
+ kv: kvs.map((binding) => ({
490
+ binding: binding.toString(),
491
+ id: "",
492
+ })),
493
+ durableObjects: durableObjects
494
+ .map((durableObject) => {
495
+ const { binding, className, scriptName } =
496
+ DURABLE_OBJECTS_BINDING_REGEXP.exec(durableObject.toString())
497
+ ?.groups || {};
498
+
499
+ if (!binding || !className) {
500
+ logger.warn(
501
+ "Could not parse Durable Object binding:",
502
+ durableObject.toString()
503
+ );
504
+ return;
505
+ }
208
506
 
209
- const { stop, waitUntilExit } = await unstable_dev(
210
- scriptPath,
211
- {
212
- port,
213
- watch: true,
214
- localProtocol,
215
- liveReload,
216
-
217
- compatibilityDate: "2021-11-02",
218
- nodeCompat,
219
- vars: Object.fromEntries(
220
- bindings
221
- .map((binding) => binding.toString().split("="))
222
- .map(([key, ...values]) => [key, values.join("=")])
223
- ),
224
- kv: kvs.map((val) => ({
225
- binding: val.toString(),
226
- id: "",
227
- })),
228
- durableObjects: durableObjects.map((durableObject) => {
229
- const [name, class_name] = durableObject.toString().split("=");
230
507
  return {
231
- name,
232
- class_name,
508
+ name: binding,
509
+ class_name: className,
510
+ script_name: scriptName,
233
511
  };
234
- }),
235
-
236
- enablePagesAssetsServiceBinding: {
237
- proxyPort,
238
- directory,
239
- },
240
- forceLocal: true,
241
- showInteractiveDevSession: undefined,
242
- inspect: true,
243
- logLevel: "error",
244
- logPrefix: "pages",
512
+ })
513
+ .filter(Boolean) as AdditionalDevProps["durableObjects"],
514
+ r2: r2s.map((binding) => {
515
+ return { binding: binding.toString(), bucket_name: "" };
516
+ }),
517
+
518
+ d1Databases: d1s.map((binding) => ({
519
+ binding: binding.toString(),
520
+ database_id: "", // Required for types, but unused by dev
521
+ database_name: `local-${binding}`,
522
+ })),
523
+
524
+ enablePagesAssetsServiceBinding: {
525
+ proxyPort,
526
+ directory,
245
527
  },
246
- true
247
- );
528
+ forceLocal: true,
529
+ persist,
530
+ persistTo,
531
+ showInteractiveDevSession: undefined,
532
+ inspect: undefined,
533
+ logPrefix: "pages",
534
+ logLevel: logLevel ?? "warn",
535
+ experimental: { testMode: false, disableExperimentalWarning: true },
536
+ });
248
537
  await metrics.sendMetricsEvent("run pages dev");
249
538
 
250
- waitUntilExit().then(() => {
539
+ CLEANUP_CALLBACKS.push(stop);
540
+
541
+ void waitUntilExit().then(() => {
251
542
  CLEANUP();
252
- stop();
253
543
  process.exit(0);
254
544
  });
255
545
 
256
- process.on("exit", () => {
257
- CLEANUP();
258
- stop();
259
- });
260
- process.on("SIGINT", () => {
261
- CLEANUP();
262
- stop();
263
- });
264
- process.on("SIGTERM", () => {
265
- CLEANUP();
266
- stop();
267
- });
546
+ process.on("exit", CLEANUP);
547
+ process.on("SIGINT", CLEANUP);
548
+ process.on("SIGTERM", CLEANUP);
268
549
  };
269
550
 
270
551
  function isWindows() {
@@ -306,7 +587,8 @@ function getPort(pid: number) {
306
587
  let command: string, regExp: RegExp;
307
588
 
308
589
  if (isWindows()) {
309
- command = "\\windows\\system32\\netstat.exe -nao";
590
+ const drive = homedir().split(":\\")[0];
591
+ command = drive + ":\\windows\\system32\\netstat.exe -nao";
310
592
  regExp = new RegExp(`TCP\\s+.*:(\\d+)\\s+.*:\\d+\\s+LISTENING\\s+${pid}`);
311
593
  } else {
312
594
  command = "lsof -nPi";
@@ -337,9 +619,13 @@ async function spawnProxyProcess({
337
619
  command: (string | number)[];
338
620
  }): Promise<undefined | number> {
339
621
  if (command.length === 0) {
622
+ if (port !== undefined) {
623
+ return port;
624
+ }
625
+
340
626
  CLEANUP();
341
627
  throw new FatalError(
342
- "Must specify a directory of static assets to serve or a command to run.",
628
+ "Must specify a directory of static assets to serve or a command to run or a proxy port.",
343
629
  1
344
630
  );
345
631
  }
@@ -370,6 +656,8 @@ async function spawnProxyProcess({
370
656
 
371
657
  proxy.on("close", (code) => {
372
658
  logger.error(`Proxy exited with status ${code}.`);
659
+ CLEANUP();
660
+ process.exitCode = code ?? 0;
373
661
  });
374
662
 
375
663
  // Wait for proxy process to start...
@@ -399,3 +687,24 @@ async function spawnProxyProcess({
399
687
 
400
688
  return port;
401
689
  }
690
+
691
+ // TODO: Kill this once we have https://github.com/cloudflare/wrangler2/issues/2153
692
+ const blockWorkerJsImports: esbuild.Plugin = {
693
+ name: "block-worker-js-imports",
694
+ setup(build) {
695
+ build.onResolve({ filter: /.*/g }, (args) => {
696
+ // If it's the entrypoint, let it be as is
697
+ if (args.kind === "entry-point") {
698
+ return {
699
+ path: args.path,
700
+ };
701
+ }
702
+ // Otherwise, block any imports that the file is requesting
703
+ logger.error(
704
+ `_worker.js is importing from another file. This will throw an error if deployed.\nYou should bundle your Worker or remove the import if it is unused.`
705
+ );
706
+ // Miniflare will error with this briefly down the line -- there's no point in continuing.
707
+ process.exit(1);
708
+ });
709
+ },
710
+ };