wrangler 0.0.0-e61fba8 → 0.0.0-e62b097bb

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 (155) hide show
  1. package/README.md +42 -36
  2. package/bin/wrangler.js +86 -67
  3. package/config-schema.json +3370 -0
  4. package/package.json +132 -103
  5. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  6. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  7. package/templates/__tests__/tsconfig.json +8 -0
  8. package/templates/checked-fetch.js +30 -0
  9. package/templates/facade.d.ts +19 -0
  10. package/templates/gitignore +2 -3
  11. package/templates/init-tests/test-jest-new-worker.js +23 -0
  12. package/templates/init-tests/test-vitest-new-worker.js +24 -0
  13. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  14. package/templates/middleware/common.ts +67 -0
  15. package/templates/middleware/loader-modules.ts +134 -0
  16. package/templates/middleware/loader-sw.ts +229 -0
  17. package/templates/middleware/middleware-ensure-req-body-drained.ts +18 -0
  18. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  19. package/templates/middleware/middleware-mock-analytics-engine.d.ts +3 -0
  20. package/templates/middleware/middleware-mock-analytics-engine.ts +30 -0
  21. package/templates/middleware/middleware-patch-console-prefix.d.ts +3 -0
  22. package/templates/middleware/middleware-patch-console-prefix.ts +21 -0
  23. package/templates/middleware/middleware-pretty-error.ts +40 -0
  24. package/templates/middleware/middleware-scheduled.ts +29 -0
  25. package/templates/middleware/middleware-serve-static-assets.d.ts +6 -0
  26. package/templates/middleware/middleware-serve-static-assets.ts +56 -0
  27. package/templates/modules-watch-stub.js +4 -0
  28. package/templates/new-worker-scheduled.js +3 -3
  29. package/templates/new-worker-scheduled.ts +16 -16
  30. package/templates/new-worker.js +3 -3
  31. package/templates/new-worker.ts +19 -16
  32. package/templates/no-op-worker.js +10 -0
  33. package/templates/pages-dev-pipeline.ts +32 -0
  34. package/templates/pages-dev-util.ts +55 -0
  35. package/templates/pages-shim.ts +9 -0
  36. package/templates/pages-template-plugin.ts +190 -0
  37. package/templates/pages-template-worker.ts +198 -0
  38. package/templates/startDevWorker/InspectorProxyWorker.ts +664 -0
  39. package/templates/startDevWorker/ProxyWorker.ts +336 -0
  40. package/templates/tsconfig-sanity.ts +11 -0
  41. package/templates/tsconfig.init.json +22 -0
  42. package/templates/tsconfig.json +6 -103
  43. package/wrangler-dist/InspectorProxyWorker.js +464 -0
  44. package/wrangler-dist/InspectorProxyWorker.js.map +6 -0
  45. package/wrangler-dist/ProxyWorker.js +241 -0
  46. package/wrangler-dist/ProxyWorker.js.map +6 -0
  47. package/wrangler-dist/cli.d.ts +26730 -0
  48. package/wrangler-dist/cli.js +165991 -119517
  49. package/Cloudflare_CA.pem +0 -18
  50. package/import_meta_url.js +0 -3
  51. package/miniflare-config-stubs/.env.empty +0 -0
  52. package/miniflare-config-stubs/package.empty.json +0 -1
  53. package/miniflare-config-stubs/wrangler.empty.toml +0 -0
  54. package/miniflare-dist/index.mjs +0 -4886
  55. package/pages/functions/buildPlugin.ts +0 -105
  56. package/pages/functions/buildWorker.ts +0 -151
  57. package/pages/functions/filepath-routing.test.ts +0 -234
  58. package/pages/functions/filepath-routing.ts +0 -189
  59. package/pages/functions/identifiers.ts +0 -78
  60. package/pages/functions/routes.ts +0 -156
  61. package/pages/functions/template-plugin.ts +0 -147
  62. package/pages/functions/template-worker.ts +0 -143
  63. package/src/__tests__/config-cache.test.ts +0 -36
  64. package/src/__tests__/configuration.test.ts +0 -3487
  65. package/src/__tests__/dev.test.tsx +0 -1010
  66. package/src/__tests__/guess-worker-format.test.ts +0 -85
  67. package/src/__tests__/helpers/clipboardy-mock.js +0 -4
  68. package/src/__tests__/helpers/cmd-shim.d.ts +0 -11
  69. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  70. package/src/__tests__/helpers/mock-account-id.ts +0 -48
  71. package/src/__tests__/helpers/mock-bin.ts +0 -36
  72. package/src/__tests__/helpers/mock-cfetch.ts +0 -211
  73. package/src/__tests__/helpers/mock-console.ts +0 -87
  74. package/src/__tests__/helpers/mock-dialogs.ts +0 -130
  75. package/src/__tests__/helpers/mock-http-server.ts +0 -46
  76. package/src/__tests__/helpers/mock-istty.ts +0 -27
  77. package/src/__tests__/helpers/mock-kv.ts +0 -33
  78. package/src/__tests__/helpers/mock-oauth-flow.ts +0 -255
  79. package/src/__tests__/helpers/mock-stdin.ts +0 -103
  80. package/src/__tests__/helpers/mock-web-socket.ts +0 -29
  81. package/src/__tests__/helpers/run-in-tmp.ts +0 -39
  82. package/src/__tests__/helpers/run-wrangler.ts +0 -16
  83. package/src/__tests__/helpers/write-worker-source.ts +0 -31
  84. package/src/__tests__/helpers/write-wrangler-toml.ts +0 -17
  85. package/src/__tests__/https-options.test.ts +0 -130
  86. package/src/__tests__/index.test.ts +0 -257
  87. package/src/__tests__/init.test.ts +0 -1637
  88. package/src/__tests__/jest.setup.ts +0 -77
  89. package/src/__tests__/kv.test.ts +0 -1502
  90. package/src/__tests__/logger.test.ts +0 -115
  91. package/src/__tests__/package-manager.test.ts +0 -353
  92. package/src/__tests__/pages.test.ts +0 -754
  93. package/src/__tests__/parse.test.ts +0 -330
  94. package/src/__tests__/publish.test.ts +0 -5820
  95. package/src/__tests__/r2.test.ts +0 -233
  96. package/src/__tests__/route.test.ts +0 -45
  97. package/src/__tests__/secret.test.ts +0 -481
  98. package/src/__tests__/tail.test.ts +0 -690
  99. package/src/__tests__/user.test.ts +0 -131
  100. package/src/__tests__/whoami.test.tsx +0 -139
  101. package/src/abort.d.ts +0 -3
  102. package/src/bundle.ts +0 -204
  103. package/src/cfetch/index.ts +0 -121
  104. package/src/cfetch/internal.ts +0 -142
  105. package/src/cli.ts +0 -13
  106. package/src/config/README.md +0 -107
  107. package/src/config/config.ts +0 -234
  108. package/src/config/diagnostics.ts +0 -80
  109. package/src/config/environment.ts +0 -420
  110. package/src/config/index.ts +0 -225
  111. package/src/config/validation-helpers.ts +0 -584
  112. package/src/config/validation.ts +0 -1548
  113. package/src/config-cache.ts +0 -45
  114. package/src/create-worker-preview.ts +0 -185
  115. package/src/create-worker-upload-form.ts +0 -254
  116. package/src/dev/dev-vars.ts +0 -36
  117. package/src/dev/dev.tsx +0 -395
  118. package/src/dev/local.tsx +0 -321
  119. package/src/dev/remote.tsx +0 -240
  120. package/src/dev/use-esbuild.ts +0 -115
  121. package/src/dialogs.tsx +0 -135
  122. package/src/durable.ts +0 -102
  123. package/src/entry.ts +0 -322
  124. package/src/environment-variables.ts +0 -35
  125. package/src/errors.ts +0 -11
  126. package/src/https-options.ts +0 -123
  127. package/src/index.tsx +0 -2859
  128. package/src/inspect.ts +0 -662
  129. package/src/intl-polyfill.d.ts +0 -139
  130. package/src/kv.ts +0 -408
  131. package/src/logger.ts +0 -66
  132. package/src/miniflare-cli/README.md +0 -30
  133. package/src/miniflare-cli/enum-keys.ts +0 -17
  134. package/src/miniflare-cli/index.ts +0 -41
  135. package/src/miniflare-cli/request-context.ts +0 -40
  136. package/src/module-collection.ts +0 -258
  137. package/src/open-in-browser.ts +0 -19
  138. package/src/package-manager.ts +0 -174
  139. package/src/pages.tsx +0 -2093
  140. package/src/parse.ts +0 -222
  141. package/src/paths.ts +0 -26
  142. package/src/proxy.ts +0 -478
  143. package/src/publish.ts +0 -721
  144. package/src/r2.ts +0 -50
  145. package/src/selfsigned.d.ts +0 -29
  146. package/src/sites.tsx +0 -326
  147. package/src/tail/filters.ts +0 -279
  148. package/src/tail/index.ts +0 -299
  149. package/src/tail/printing.ts +0 -67
  150. package/src/update-check.ts +0 -19
  151. package/src/user.tsx +0 -1214
  152. package/src/whoami.tsx +0 -71
  153. package/src/worker.ts +0 -181
  154. package/src/zones.ts +0 -73
  155. package/templates/static-asset-facade.js +0 -43
package/src/dev/dev.tsx DELETED
@@ -1,395 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import * as path from "node:path";
3
- import { watch } from "chokidar";
4
- import clipboardy from "clipboardy";
5
- import commandExists from "command-exists";
6
- import { Box, Text, useApp, useInput, useStdin } from "ink";
7
- import React, { useState, useEffect, useRef } from "react";
8
- import { withErrorBoundary, useErrorHandler } from "react-error-boundary";
9
- import onExit from "signal-exit";
10
- import tmp from "tmp-promise";
11
- import { fetch } from "undici";
12
- import { runCustomBuild } from "../entry";
13
- import { openInspector } from "../inspect";
14
- import { logger } from "../logger";
15
- import openInBrowser from "../open-in-browser";
16
- import { Local } from "./local";
17
- import { Remote } from "./remote";
18
- import { useEsbuild } from "./use-esbuild";
19
- import type { Config } from "../config";
20
- import type { Entry } from "../entry";
21
- import type { AssetPaths } from "../sites";
22
- import type { CfWorkerInit } from "../worker";
23
-
24
- export type DevProps = {
25
- name?: string;
26
- entry: Entry;
27
- port: number;
28
- ip: string;
29
- inspectorPort: number;
30
- rules: Config["rules"];
31
- accountId: string | undefined;
32
- initialMode: "local" | "remote";
33
- jsxFactory: string | undefined;
34
- jsxFragment: string | undefined;
35
- tsconfig: string | undefined;
36
- upstreamProtocol: "https" | "http";
37
- localProtocol: "https" | "http";
38
- enableLocalPersistence: boolean;
39
- bindings: CfWorkerInit["bindings"];
40
- crons: Config["triggers"]["crons"];
41
- public: string | undefined;
42
- assetPaths: AssetPaths | undefined;
43
- compatibilityDate: string;
44
- compatibilityFlags: string[] | undefined;
45
- usageModel: "bundled" | "unbound" | undefined;
46
- minify: boolean | undefined;
47
- nodeCompat: boolean | undefined;
48
- build: Config["build"];
49
- env: string | undefined;
50
- legacyEnv: boolean;
51
- zone: string | undefined;
52
- host: string | undefined;
53
- };
54
-
55
- export function DevImplementation(props: DevProps): JSX.Element {
56
- if (props.public && props.entry.format === "service-worker") {
57
- throw new Error(
58
- "You cannot use the service-worker format with a `public` directory."
59
- );
60
- }
61
-
62
- if (props.bindings.wasm_modules && props.entry.format === "modules") {
63
- throw new Error(
64
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
65
- );
66
- }
67
-
68
- if (props.bindings.text_blobs && props.entry.format === "modules") {
69
- throw new Error(
70
- "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
71
- );
72
- }
73
-
74
- if (props.bindings.data_blobs && props.entry.format === "modules") {
75
- throw new Error(
76
- "You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
77
- );
78
- }
79
-
80
- // only load the UI if we're running in a supported environment
81
- const { isRawModeSupported } = useStdin();
82
- return isRawModeSupported ? (
83
- <InteractiveDevSession {...props} />
84
- ) : (
85
- <DevSession {...props} local={props.initialMode === "local"} />
86
- );
87
- }
88
-
89
- function InteractiveDevSession(props: DevProps) {
90
- const toggles = useHotkeys(
91
- {
92
- local: props.initialMode === "local",
93
- tunnel: false,
94
- },
95
- props.port,
96
- props.ip,
97
- props.inspectorPort,
98
- props.localProtocol
99
- );
100
-
101
- useTunnel(toggles.tunnel);
102
-
103
- return (
104
- <>
105
- <DevSession {...props} local={toggles.local} />
106
- <Box borderStyle="round" paddingLeft={1} paddingRight={1}>
107
- <Text bold={true}>[b]</Text>
108
- <Text> open a browser, </Text>
109
- <Text bold={true}>[d]</Text>
110
- <Text> open Devtools, </Text>
111
- <Text bold={true}>[l]</Text>
112
- <Text> {toggles.local ? "turn off" : "turn on"} local mode, </Text>
113
- <Text bold={true}>[c]</Text>
114
- <Text> clear console, </Text>
115
- <Text bold={true}>[x]</Text>
116
- <Text> to exit</Text>
117
- </Box>
118
- </>
119
- );
120
- }
121
-
122
- type DevSessionProps = DevProps & {
123
- local: boolean;
124
- };
125
-
126
- function DevSession(props: DevSessionProps) {
127
- useCustomBuild(props.entry, props.build);
128
-
129
- const directory = useTmpDir();
130
-
131
- const bundle = useEsbuild({
132
- entry: props.entry,
133
- destination: directory,
134
- staticRoot: props.public,
135
- jsxFactory: props.jsxFactory,
136
- rules: props.rules,
137
- jsxFragment: props.jsxFragment,
138
- // In dev for remote mode, we serve --experimental-assets from the local proxy before we send the request to the worker.
139
- serveAssetsFromWorker: !!props.public && !!props.local,
140
- tsconfig: props.tsconfig,
141
- minify: props.minify,
142
- nodeCompat: props.nodeCompat,
143
- });
144
-
145
- return props.local ? (
146
- <Local
147
- name={props.name}
148
- bundle={bundle}
149
- format={props.entry.format}
150
- compatibilityDate={props.compatibilityDate}
151
- localProtocol={props.localProtocol}
152
- compatibilityFlags={props.compatibilityFlags}
153
- bindings={props.bindings}
154
- assetPaths={props.assetPaths}
155
- public={props.public}
156
- port={props.port}
157
- ip={props.ip}
158
- rules={props.rules}
159
- inspectorPort={props.inspectorPort}
160
- enableLocalPersistence={props.enableLocalPersistence}
161
- crons={props.crons}
162
- />
163
- ) : (
164
- <Remote
165
- name={props.name}
166
- bundle={bundle}
167
- format={props.entry.format}
168
- accountId={props.accountId}
169
- bindings={props.bindings}
170
- assetPaths={props.assetPaths}
171
- public={props.public}
172
- port={props.port}
173
- ip={props.ip}
174
- localProtocol={props.localProtocol}
175
- inspectorPort={props.inspectorPort}
176
- compatibilityDate={props.compatibilityDate}
177
- compatibilityFlags={props.compatibilityFlags}
178
- usageModel={props.usageModel}
179
- env={props.env}
180
- legacyEnv={props.legacyEnv}
181
- zone={props.zone}
182
- host={props.host}
183
- />
184
- );
185
- }
186
-
187
- export interface DirectorySyncResult {
188
- name: string;
189
- removeCallback: () => void;
190
- }
191
-
192
- function useTmpDir(): string | undefined {
193
- const [directory, setDirectory] = useState<DirectorySyncResult>();
194
- const handleError = useErrorHandler();
195
- useEffect(() => {
196
- let dir: DirectorySyncResult | undefined;
197
- try {
198
- dir = tmp.dirSync({ unsafeCleanup: true });
199
- setDirectory(dir);
200
- return;
201
- } catch (err) {
202
- logger.error(
203
- "Failed to create temporary directory to store built files."
204
- );
205
- handleError(err);
206
- }
207
- return () => {
208
- dir?.removeCallback();
209
- };
210
- }, [handleError]);
211
- return directory?.name;
212
- }
213
-
214
- function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
215
- useEffect(() => {
216
- if (!build.command) return;
217
- let watcher: ReturnType<typeof watch> | undefined;
218
- if (build.watch_dir) {
219
- watcher = watch(build.watch_dir, {
220
- persistent: true,
221
- ignoreInitial: true,
222
- }).on("all", (_event, filePath) => {
223
- const relativeFile =
224
- path.relative(expectedEntry.directory, expectedEntry.file) || ".";
225
- //TODO: we should buffer requests to the proxy until this completes
226
- logger.log(`The file ${filePath} changed, restarting build...`);
227
- runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
228
- logger.error("Custom build failed:", err);
229
- });
230
- });
231
- }
232
-
233
- return () => {
234
- watcher?.close();
235
- };
236
- }, [build, expectedEntry]);
237
- }
238
-
239
- function sleep(period: number) {
240
- return new Promise((resolve) => setTimeout(resolve, period));
241
- }
242
- const SLEEP_DURATION = 2000;
243
- // really need a first class api for this
244
- const hostNameRegex = /userHostname="(.*)"/g;
245
- async function findTunnelHostname() {
246
- let hostName: string | undefined;
247
- while (!hostName) {
248
- try {
249
- const resp = await fetch("http://localhost:8789/metrics");
250
- const data = await resp.text();
251
- const matches = Array.from(data.matchAll(hostNameRegex));
252
- hostName = matches[0][1];
253
- } catch (err) {
254
- await sleep(SLEEP_DURATION);
255
- }
256
- }
257
- return hostName;
258
- }
259
-
260
- /**
261
- * Create a tunnel to the remote worker.
262
- * We've disabled this for now until we figure out a better user experience.
263
- */
264
- function useTunnel(toggle: boolean) {
265
- const tunnel = useRef<ReturnType<typeof spawn>>();
266
- const removeSignalExitListener = useRef<() => void>();
267
- // TODO: test if cloudflared is available, if not
268
- // point them to a url where they can get docs to install it
269
- useEffect(() => {
270
- async function startTunnel() {
271
- if (toggle) {
272
- try {
273
- await commandExists("cloudflared");
274
- } catch (e) {
275
- logger.warn(
276
- "To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
277
- );
278
- return;
279
- }
280
- logger.log("⎔ Starting a tunnel...");
281
- tunnel.current = spawn("cloudflared", [
282
- "tunnel",
283
- "--url",
284
- "http://localhost:8787",
285
- "--metrics",
286
- "localhost:8789",
287
- ]);
288
-
289
- tunnel.current.on("close", (code) => {
290
- if (code) {
291
- logger.log(`Tunnel process exited with code ${code}`);
292
- }
293
- });
294
-
295
- removeSignalExitListener.current = onExit((_code, _signal) => {
296
- logger.log("⎔ Shutting down local tunnel.");
297
- tunnel.current?.kill();
298
- tunnel.current = undefined;
299
- });
300
-
301
- const hostName = await findTunnelHostname();
302
- await clipboardy.write(hostName);
303
- logger.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
304
- }
305
- }
306
-
307
- startTunnel().catch(async (err) => {
308
- logger.error("tunnel:", err);
309
- });
310
-
311
- return () => {
312
- if (tunnel.current) {
313
- logger.log("⎔ Shutting down tunnel.");
314
- tunnel.current?.kill();
315
- tunnel.current = undefined;
316
- removeSignalExitListener.current && removeSignalExitListener.current();
317
- removeSignalExitListener.current = undefined;
318
- }
319
- };
320
- }, [toggle]);
321
- }
322
-
323
- type useHotkeysInitialState = {
324
- local: boolean;
325
- tunnel: boolean;
326
- };
327
- function useHotkeys(
328
- initial: useHotkeysInitialState,
329
- port: number,
330
- ip: string,
331
- inspectorPort: number,
332
- localProtocol: "http" | "https"
333
- ) {
334
- // UGH, we should put port in context instead
335
- const [toggles, setToggles] = useState(initial);
336
- const { exit } = useApp();
337
- useInput(
338
- async (
339
- input,
340
- // eslint-disable-next-line unused-imports/no-unused-vars
341
- key
342
- ) => {
343
- switch (input.toLowerCase()) {
344
- // clear console
345
- case "c":
346
- console.clear();
347
- // This console.log causes Ink to re-render the `DevSession` component.
348
- // Couldn't find a better way to tell it to do so...
349
- console.log();
350
- break;
351
- // open browser
352
- case "b": {
353
- await openInBrowser(`${localProtocol}://${ip}:${port}`);
354
- break;
355
- }
356
- // toggle inspector
357
- case "d": {
358
- await openInspector(inspectorPort);
359
- break;
360
- }
361
- // toggle local
362
- case "l":
363
- setToggles((previousToggles) => ({
364
- ...previousToggles,
365
- local: !previousToggles.local,
366
- }));
367
- break;
368
- // shut down
369
- case "q":
370
- case "x":
371
- exit();
372
- break;
373
- default:
374
- // nothing?
375
- break;
376
- }
377
- }
378
- );
379
- return toggles;
380
- }
381
-
382
- function ErrorFallback(props: { error: Error }) {
383
- const { exit } = useApp();
384
- useEffect(() => exit(props.error));
385
- return (
386
- <>
387
- <Text>Something went wrong:</Text>
388
- <Text>{props.error.stack}</Text>
389
- </>
390
- );
391
- }
392
-
393
- export default withErrorBoundary(DevImplementation, {
394
- FallbackComponent: ErrorFallback,
395
- });
package/src/dev/local.tsx DELETED
@@ -1,321 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { realpathSync } from "node:fs";
3
- import { writeFile } from "node:fs/promises";
4
- import path from "node:path";
5
- import { useState, useEffect, useRef } from "react";
6
- import onExit from "signal-exit";
7
- import useInspector from "../inspect";
8
- import { logger } from "../logger";
9
- import { DEFAULT_MODULE_RULES } from "../module-collection";
10
- import { waitForPortToBeAvailable } from "../proxy";
11
- import type { Config } from "../config";
12
- import type { AssetPaths } from "../sites";
13
- import type { CfWorkerInit, CfScriptFormat } from "../worker";
14
- import type { EsbuildBundle } from "./use-esbuild";
15
- import type { MiniflareOptions } from "miniflare";
16
-
17
- interface LocalProps {
18
- name: string | undefined;
19
- bundle: EsbuildBundle | undefined;
20
- format: CfScriptFormat | undefined;
21
- compatibilityDate: string;
22
- compatibilityFlags: string[] | undefined;
23
- bindings: CfWorkerInit["bindings"];
24
- localProtocol: "http" | "https";
25
- assetPaths: AssetPaths | undefined;
26
- public: string | undefined;
27
- port: number;
28
- ip: string;
29
- rules: Config["rules"];
30
- inspectorPort: number;
31
- enableLocalPersistence: boolean;
32
- crons: Config["triggers"]["crons"];
33
- }
34
-
35
- export function Local(props: LocalProps) {
36
- const { inspectorUrl } = useLocalWorker(props);
37
- useInspector({
38
- inspectorUrl,
39
- port: props.inspectorPort,
40
- logToTerminal: false,
41
- });
42
- return null;
43
- }
44
-
45
- function useLocalWorker({
46
- name: workerName,
47
- bundle,
48
- format,
49
- compatibilityDate,
50
- compatibilityFlags,
51
- bindings,
52
- assetPaths,
53
- public: publicDirectory,
54
- port,
55
- rules,
56
- enableLocalPersistence,
57
- ip,
58
- localProtocol,
59
- crons,
60
- }: LocalProps) {
61
- // TODO: pass vars via command line
62
- const local = useRef<ReturnType<typeof spawn>>();
63
- const removeSignalExitListener = useRef<() => void>();
64
- const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
65
- // if we're using local persistence for data, we should use the cwd
66
- // as an explicit path, or else it'll use the temp dir
67
- // which disappears when dev ends
68
- const localPersistencePath = enableLocalPersistence
69
- ? // Maybe we could make the path configurable as well?
70
- path.join(process.cwd(), "wrangler-local-state")
71
- : // We otherwise choose null, but choose true later
72
- // so that it's persisted in the temp dir across a dev session
73
- // even when we change source and reload
74
- null;
75
- useEffect(() => {
76
- const abortController = new AbortController();
77
- async function startLocalWorker() {
78
- if (!bundle || !format) return;
79
-
80
- // port for the worker
81
- await waitForPortToBeAvailable(port, {
82
- retryPeriod: 200,
83
- timeout: 2000,
84
- abortSignal: abortController.signal,
85
- });
86
-
87
- if (bindings.services && bindings.services.length > 0) {
88
- throw new Error(
89
- "⎔ Service bindings are not yet supported in local mode."
90
- );
91
- }
92
-
93
- // In local mode, we want to copy all referenced modules into
94
- // the output bundle directory before starting up
95
- for (const module of bundle.modules) {
96
- await writeFile(
97
- path.join(path.dirname(bundle.path), module.name),
98
- module.content
99
- );
100
- }
101
-
102
- const scriptPath = realpathSync(bundle.path);
103
-
104
- // the wasm_modules/text_blobs/data_blobs bindings are
105
- // relative to process.cwd(), but the actual worker bundle
106
- // is in the temp output directory; so we rewrite the paths to be absolute,
107
- // letting miniflare resolve them correctly
108
-
109
- // wasm
110
- const wasmBindings: Record<string, string> = {};
111
- for (const [name, filePath] of Object.entries(
112
- bindings.wasm_modules || {}
113
- )) {
114
- wasmBindings[name] = path.join(process.cwd(), filePath);
115
- }
116
-
117
- // text
118
- const textBlobBindings: Record<string, string> = {};
119
- for (const [name, filePath] of Object.entries(
120
- bindings.text_blobs || {}
121
- )) {
122
- textBlobBindings[name] = path.join(process.cwd(), filePath);
123
- }
124
-
125
- // data
126
- const dataBlobBindings: Record<string, string> = {};
127
- for (const [name, filePath] of Object.entries(
128
- bindings.data_blobs || {}
129
- )) {
130
- dataBlobBindings[name] = path.join(process.cwd(), filePath);
131
- }
132
-
133
- if (format === "service-worker") {
134
- for (const { type, name } of bundle.modules) {
135
- if (type === "compiled-wasm") {
136
- // In service-worker format, .wasm modules are referenced by global identifiers,
137
- // so we convert it here.
138
- // This identifier has to be a valid JS identifier, so we replace all non alphanumeric
139
- // characters with an underscore.
140
- const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
141
- wasmBindings[identifier] = name;
142
- } else if (type === "text") {
143
- // In service-worker format, text modules are referenced by global identifiers,
144
- // so we convert it here.
145
- // This identifier has to be a valid JS identifier, so we replace all non alphanumeric
146
- // characters with an underscore.
147
- const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
148
- textBlobBindings[identifier] = name;
149
- } else if (type === "buffer") {
150
- // In service-worker format, data blobs are referenced by global identifiers,
151
- // so we convert it here.
152
- // This identifier has to be a valid JS identifier, so we replace all non alphanumeric
153
- // characters with an underscore.
154
- const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
155
- dataBlobBindings[identifier] = name;
156
- }
157
- }
158
- }
159
-
160
- const options: MiniflareOptions = {
161
- name: workerName,
162
- port,
163
- scriptPath,
164
- https: localProtocol === "https",
165
- host: ip,
166
- modules: format === "modules",
167
- modulesRules: (rules || [])
168
- .concat(DEFAULT_MODULE_RULES)
169
- .map(({ type, globs: include, fallthrough }) => ({
170
- type,
171
- include,
172
- fallthrough,
173
- })),
174
- compatibilityDate,
175
- compatibilityFlags,
176
- kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
177
- durableObjects: Object.fromEntries(
178
- (bindings.durable_objects?.bindings ?? []).map<[string, string]>(
179
- (value) => [value.name, value.class_name]
180
- )
181
- ),
182
- ...(localPersistencePath
183
- ? {
184
- kvPersist: path.join(localPersistencePath, "kv"),
185
- durableObjectsPersist: path.join(localPersistencePath, "do"),
186
- cachePersist: path.join(localPersistencePath, "cache"),
187
- }
188
- : {
189
- // We mark these as true, so that they'll
190
- // persist in the temp directory.
191
- // This means they'll persist across a dev session,
192
- // even if we change source and reload,
193
- // and be deleted when the dev session ends
194
- durableObjectsPersist: true,
195
- cachePersist: true,
196
- kvPersist: true,
197
- }),
198
-
199
- sitePath: assetPaths?.assetDirectory
200
- ? path.join(assetPaths.baseDirectory, assetPaths.assetDirectory)
201
- : undefined,
202
- siteInclude: assetPaths?.includePatterns.length
203
- ? assetPaths?.includePatterns
204
- : undefined,
205
- siteExclude: assetPaths?.excludePatterns.length
206
- ? assetPaths.excludePatterns
207
- : undefined,
208
- bindings: bindings.vars,
209
- wasmBindings,
210
- textBlobBindings,
211
- dataBlobBindings,
212
- sourceMap: true,
213
- logUnhandledRejections: true,
214
- crons,
215
- };
216
-
217
- // The path to the Miniflare CLI assumes that this file is being run from
218
- // `wrangler-dist` and that the CLI is found in `miniflare-dist`.
219
- // If either of those paths change this line needs updating.
220
- const miniflareCLIPath = path.resolve(
221
- __dirname,
222
- "../miniflare-dist/index.mjs"
223
- );
224
- const optionsArg = JSON.stringify(options, null);
225
-
226
- logger.log("⎔ Starting a local server...");
227
- local.current = spawn(
228
- "node",
229
- [
230
- "--experimental-vm-modules", // ensures that Miniflare can run ESM Workers
231
- "--no-warnings", // hide annoying Node warnings
232
- "--inspect", // start Miniflare listening for a debugger to attach
233
- miniflareCLIPath,
234
- optionsArg,
235
- // "--log=VERBOSE", // uncomment this to Miniflare to log "everything"!
236
- ],
237
- {
238
- cwd: path.dirname(scriptPath),
239
- }
240
- );
241
-
242
- local.current.on("close", (code) => {
243
- if (code) {
244
- logger.log(`Miniflare process exited with code ${code}`);
245
- }
246
- });
247
-
248
- local.current.stdout?.on("data", (data: Buffer) => {
249
- process.stdout.write(data);
250
- });
251
-
252
- // parse the node inspector url (which may be received in chunks) from stderr
253
- let stderrData = "";
254
- local.current.stderr?.on("data", (data: Buffer) => {
255
- stderrData += data.toString();
256
- const matches =
257
- /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)[\r|\n]/.exec(
258
- stderrData
259
- );
260
- if (matches) {
261
- local.current?.stderr?.removeAllListeners("data");
262
- setInspectorUrl(matches[1]);
263
- }
264
- });
265
-
266
- local.current.on("exit", (code) => {
267
- if (code) {
268
- logger.error(`Miniflare process exited with code ${code}`);
269
- }
270
- });
271
-
272
- local.current.on("error", (error: Error) => {
273
- logger.error(`Miniflare process failed to spawn`);
274
- logger.error(error);
275
- });
276
-
277
- removeSignalExitListener.current = onExit((_code, _signal) => {
278
- logger.log("⎔ Shutting down local server.");
279
- local.current?.kill();
280
- local.current = undefined;
281
- });
282
- }
283
-
284
- startLocalWorker().catch((err) => {
285
- logger.error("local worker:", err);
286
- });
287
-
288
- return () => {
289
- abortController.abort();
290
- if (local.current) {
291
- logger.log("⎔ Shutting down local server.");
292
- local.current?.kill();
293
- local.current = undefined;
294
- removeSignalExitListener.current && removeSignalExitListener.current();
295
- removeSignalExitListener.current = undefined;
296
- }
297
- };
298
- }, [
299
- bundle,
300
- workerName,
301
- format,
302
- port,
303
- localProtocol,
304
- ip,
305
- bindings.durable_objects?.bindings,
306
- bindings.kv_namespaces,
307
- bindings.vars,
308
- bindings.services,
309
- compatibilityDate,
310
- compatibilityFlags,
311
- localPersistencePath,
312
- assetPaths,
313
- publicDirectory,
314
- rules,
315
- bindings.wasm_modules,
316
- bindings.text_blobs,
317
- bindings.data_blobs,
318
- crons,
319
- ]);
320
- return { inspectorUrl };
321
- }