wrangler 0.0.0-e1c615f → 0.0.0-e3cab74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/wrangler.js +2 -2
- package/package.json +28 -13
- package/pages/functions/buildWorker.ts +6 -1
- package/pages/functions/filepath-routing.test.ts +112 -28
- package/pages/functions/filepath-routing.ts +88 -131
- package/pages/functions/routes.ts +11 -18
- package/pages/functions/template-worker.ts +21 -28
- package/src/__tests__/configuration.test.ts +2185 -0
- package/src/__tests__/dev.test.tsx +56 -9
- package/src/__tests__/guess-worker-format.test.ts +66 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +30 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +53 -17
- package/src/__tests__/helpers/mock-console.ts +62 -0
- package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
- package/src/__tests__/helpers/mock-kv.ts +40 -0
- package/src/__tests__/helpers/mock-user.ts +27 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +29 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/index.test.ts +655 -76
- package/src/__tests__/jest.setup.ts +37 -2
- package/src/__tests__/kv.test.ts +441 -398
- package/src/__tests__/logout.test.ts +50 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/parse.test.ts +322 -0
- package/src/__tests__/publish.test.ts +2406 -0
- package/src/__tests__/r2.test.ts +198 -0
- package/src/__tests__/secret.test.ts +326 -0
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +564 -0
- package/src/__tests__/whoami.test.tsx +86 -0
- package/src/api/form_data.ts +112 -26
- package/src/api/preview.ts +34 -15
- package/src/api/worker.ts +58 -27
- package/src/bundle.ts +133 -0
- package/src/cfetch/index.ts +21 -19
- package/src/cfetch/internal.ts +85 -21
- package/src/cli.ts +10 -0
- package/src/config/README.md +107 -0
- package/src/config/config.ts +265 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +305 -0
- package/src/config/index.ts +52 -0
- package/src/config/validation-helpers.ts +370 -0
- package/src/config/validation.ts +1017 -0
- package/src/dev.tsx +455 -368
- package/src/dialogs.tsx +2 -2
- package/src/entry.ts +57 -0
- package/src/environment-variables.ts +33 -0
- package/src/guess-worker-format.ts +68 -0
- package/src/index.tsx +1044 -646
- package/src/inspect.ts +609 -0
- package/src/kv.tsx +47 -39
- package/src/module-collection.ts +142 -36
- package/src/open-in-browser.ts +13 -0
- package/src/package-manager.ts +126 -0
- package/src/pages.tsx +139 -62
- package/src/parse.ts +183 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +350 -77
- package/src/publish.ts +307 -289
- package/src/r2.ts +50 -0
- package/src/reporting.ts +115 -0
- package/src/sites.tsx +183 -56
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +62 -0
- package/src/user.tsx +137 -110
- package/src/whoami.tsx +69 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +127861 -111486
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/run-in-tmp.ts +0 -19
- package/src/__tests__/run-wrangler.ts +0 -32
- package/src/api/inspect.ts +0 -430
- package/src/config.ts +0 -131
- package/src/tail.tsx +0 -73
package/src/dev.tsx
CHANGED
|
@@ -1,64 +1,67 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync } from "fs";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { watch } from "chokidar";
|
|
7
|
+
import clipboardy from "clipboardy";
|
|
8
|
+
import commandExists from "command-exists";
|
|
9
|
+
import { execaCommand } from "execa";
|
|
7
10
|
import { Box, Text, useApp, useInput } from "ink";
|
|
8
11
|
import React, { useState, useEffect, useRef } from "react";
|
|
9
|
-
import
|
|
10
|
-
import open from "open";
|
|
11
|
-
import { DtInspector } from "./api/inspect";
|
|
12
|
-
import type { CfModule } from "./api/worker";
|
|
13
|
-
import { createWorker } from "./api/worker";
|
|
14
|
-
import type { CfWorkerInit } from "./api/worker";
|
|
15
|
-
import { spawn } from "child_process";
|
|
12
|
+
import { withErrorBoundary, useErrorHandler } from "react-error-boundary";
|
|
16
13
|
import onExit from "signal-exit";
|
|
14
|
+
import tmp from "tmp-promise";
|
|
15
|
+
import { fetch } from "undici";
|
|
16
|
+
import { createWorker } from "./api/worker";
|
|
17
|
+
import { bundleWorker } from "./bundle";
|
|
18
|
+
import guessWorkerFormat from "./guess-worker-format";
|
|
19
|
+
import useInspector from "./inspect";
|
|
20
|
+
import { DEFAULT_MODULE_RULES } from "./module-collection";
|
|
21
|
+
import openInBrowser from "./open-in-browser";
|
|
22
|
+
import { usePreviewServer, waitForPortToBeAvailable } from "./proxy";
|
|
17
23
|
import { syncAssets } from "./sites";
|
|
18
|
-
import clipboardy from "clipboardy";
|
|
19
|
-
import http from "node:http";
|
|
20
|
-
import commandExists from "command-exists";
|
|
21
|
-
import assert from "assert";
|
|
22
24
|
import { getAPIToken } from "./user";
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
type
|
|
25
|
+
import type { CfPreviewToken } from "./api/preview";
|
|
26
|
+
import type { CfModule, CfWorkerInit, CfScriptFormat } from "./api/worker";
|
|
27
|
+
import type { Config } from "./config";
|
|
28
|
+
import type { Entry } from "./entry";
|
|
29
|
+
import type { AssetPaths } from "./sites";
|
|
30
|
+
import type { WatchMode } from "esbuild";
|
|
31
|
+
import type { ExecaChildProcess } from "execa";
|
|
32
|
+
import type { DirectoryResult } from "tmp-promise";
|
|
31
33
|
|
|
32
34
|
export type DevProps = {
|
|
33
35
|
name?: string;
|
|
34
|
-
entry:
|
|
36
|
+
entry: Entry;
|
|
35
37
|
port?: number;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
inspectorPort: number;
|
|
39
|
+
format: CfScriptFormat | undefined;
|
|
40
|
+
rules: Config["rules"];
|
|
41
|
+
accountId: undefined | string;
|
|
38
42
|
initialMode: "local" | "remote";
|
|
39
|
-
jsxFactory:
|
|
40
|
-
jsxFragment:
|
|
43
|
+
jsxFactory: undefined | string;
|
|
44
|
+
jsxFragment: undefined | string;
|
|
45
|
+
enableLocalPersistence: boolean;
|
|
41
46
|
bindings: CfWorkerInit["bindings"];
|
|
42
|
-
public:
|
|
43
|
-
|
|
44
|
-
compatibilityDate:
|
|
45
|
-
compatibilityFlags:
|
|
46
|
-
usageModel:
|
|
47
|
+
public: undefined | string;
|
|
48
|
+
assetPaths: undefined | AssetPaths;
|
|
49
|
+
compatibilityDate: undefined | string;
|
|
50
|
+
compatibilityFlags: undefined | string[];
|
|
51
|
+
usageModel: undefined | "bundled" | "unbound";
|
|
47
52
|
buildCommand: {
|
|
48
53
|
command?: undefined | string;
|
|
49
54
|
cwd?: undefined | string;
|
|
50
55
|
watch_dir?: undefined | string;
|
|
51
56
|
};
|
|
57
|
+
env: string | undefined;
|
|
58
|
+
legacyEnv: boolean;
|
|
59
|
+
zone: string | undefined;
|
|
52
60
|
};
|
|
53
61
|
|
|
54
62
|
function Dev(props: DevProps): JSX.Element {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"You cannot use the service worker format with a `public` directory."
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
const port = props.port || 8787;
|
|
61
|
-
const apiToken = getAPIToken();
|
|
63
|
+
const port = props.port ?? 8787;
|
|
64
|
+
const apiToken = props.initialMode === "remote" ? getAPIToken() : undefined;
|
|
62
65
|
const directory = useTmpDir();
|
|
63
66
|
|
|
64
67
|
// if there isn't a build command, we just return the entry immediately
|
|
@@ -66,58 +69,81 @@ function Dev(props: DevProps): JSX.Element {
|
|
|
66
69
|
// kinda forbid that, so we thread the entry through useCustomBuild
|
|
67
70
|
const entry = useCustomBuild(props.entry, props.buildCommand);
|
|
68
71
|
|
|
72
|
+
const format = useWorkerFormat({ entry: props.entry, format: props.format });
|
|
73
|
+
if (format && props.public && format === "service-worker") {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"You cannot use the service worker format with a `public` directory."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (props.bindings.wasm_modules && format === "modules") {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (props.bindings.text_blobs && format === "modules") {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[build.upload.rules]` in your wrangler.toml"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
69
91
|
const bundle = useEsbuild({
|
|
70
92
|
entry,
|
|
93
|
+
format,
|
|
71
94
|
destination: directory,
|
|
72
95
|
staticRoot: props.public,
|
|
73
96
|
jsxFactory: props.jsxFactory,
|
|
97
|
+
rules: props.rules,
|
|
74
98
|
jsxFragment: props.jsxFragment,
|
|
99
|
+
serveAssetsFromWorker: !!props.public,
|
|
75
100
|
});
|
|
76
|
-
if (bundle && bundle.type === "commonjs" && !props.format && props.public) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
"You cannot use the service worker format with a `public` directory."
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// @ts-expect-error whack
|
|
83
|
-
useDevtoolsRefresh(bundle?.id ?? 0);
|
|
84
101
|
|
|
85
102
|
const toggles = useHotkeys(
|
|
86
103
|
{
|
|
87
104
|
local: props.initialMode === "local",
|
|
88
105
|
tunnel: false,
|
|
89
106
|
},
|
|
90
|
-
port
|
|
107
|
+
port,
|
|
108
|
+
props.inspectorPort
|
|
91
109
|
);
|
|
92
110
|
|
|
93
111
|
useTunnel(toggles.tunnel);
|
|
112
|
+
const scriptName = props.name || path.basename(props.entry.file);
|
|
94
113
|
|
|
95
114
|
return (
|
|
96
115
|
<>
|
|
97
116
|
{toggles.local ? (
|
|
98
117
|
<Local
|
|
99
|
-
name={
|
|
118
|
+
name={scriptName}
|
|
100
119
|
bundle={bundle}
|
|
101
|
-
format={
|
|
120
|
+
format={format}
|
|
102
121
|
bindings={props.bindings}
|
|
103
|
-
|
|
122
|
+
assetPaths={props.assetPaths}
|
|
104
123
|
public={props.public}
|
|
105
|
-
port={
|
|
124
|
+
port={port}
|
|
125
|
+
rules={props.rules}
|
|
126
|
+
inspectorPort={props.inspectorPort}
|
|
127
|
+
enableLocalPersistence={props.enableLocalPersistence}
|
|
106
128
|
/>
|
|
107
129
|
) : (
|
|
108
130
|
<Remote
|
|
109
131
|
name={props.name}
|
|
110
132
|
bundle={bundle}
|
|
111
|
-
format={
|
|
133
|
+
format={format}
|
|
112
134
|
accountId={props.accountId}
|
|
113
135
|
apiToken={apiToken}
|
|
114
136
|
bindings={props.bindings}
|
|
115
|
-
|
|
137
|
+
assetPaths={props.assetPaths}
|
|
116
138
|
public={props.public}
|
|
117
|
-
port={
|
|
139
|
+
port={port}
|
|
140
|
+
inspectorPort={props.inspectorPort}
|
|
118
141
|
compatibilityDate={props.compatibilityDate}
|
|
119
142
|
compatibilityFlags={props.compatibilityFlags}
|
|
120
143
|
usageModel={props.usageModel}
|
|
144
|
+
env={props.env}
|
|
145
|
+
legacyEnv={props.legacyEnv}
|
|
146
|
+
zone={props.zone}
|
|
121
147
|
/>
|
|
122
148
|
)}
|
|
123
149
|
<Box borderStyle="round" paddingLeft={1} paddingRight={1}>
|
|
@@ -133,53 +159,46 @@ function Dev(props: DevProps): JSX.Element {
|
|
|
133
159
|
);
|
|
134
160
|
}
|
|
135
161
|
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
// @ts-expect-error whack
|
|
142
|
-
ref.current = bundleId;
|
|
143
|
-
|
|
162
|
+
function useWorkerFormat(props: {
|
|
163
|
+
entry: Entry | undefined;
|
|
164
|
+
format: undefined | CfScriptFormat;
|
|
165
|
+
}): CfScriptFormat | undefined {
|
|
166
|
+
const [format, setFormat] = useState<CfScriptFormat | undefined>();
|
|
144
167
|
useEffect(() => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
res.setHeader("Access-Control-Request-Method", "*");
|
|
148
|
-
res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET");
|
|
149
|
-
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
150
|
-
if (req.method === "OPTIONS") {
|
|
151
|
-
res.writeHead(200);
|
|
152
|
-
res.end();
|
|
168
|
+
async function validateFormat() {
|
|
169
|
+
if (!props.entry || format) {
|
|
153
170
|
return;
|
|
154
171
|
}
|
|
155
|
-
|
|
156
|
-
|
|
172
|
+
setFormat(await guessWorkerFormat(props.entry, props.format));
|
|
173
|
+
}
|
|
174
|
+
validateFormat().catch((err) => {
|
|
175
|
+
console.error("Failed to validate worker format:", err);
|
|
157
176
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return () => {
|
|
161
|
-
server.close();
|
|
162
|
-
};
|
|
163
|
-
}, []);
|
|
177
|
+
}, [props.entry, props.format, format]);
|
|
178
|
+
return format;
|
|
164
179
|
}
|
|
165
180
|
|
|
166
181
|
function Remote(props: {
|
|
167
|
-
name:
|
|
168
|
-
bundle: EsbuildBundle |
|
|
169
|
-
format: CfScriptFormat;
|
|
170
|
-
public:
|
|
171
|
-
|
|
182
|
+
name: undefined | string;
|
|
183
|
+
bundle: EsbuildBundle | undefined;
|
|
184
|
+
format: CfScriptFormat | undefined;
|
|
185
|
+
public: undefined | string;
|
|
186
|
+
assetPaths: undefined | AssetPaths;
|
|
172
187
|
port: number;
|
|
173
|
-
|
|
174
|
-
|
|
188
|
+
inspectorPort: number;
|
|
189
|
+
accountId: undefined | string;
|
|
190
|
+
apiToken: undefined | string;
|
|
175
191
|
bindings: CfWorkerInit["bindings"];
|
|
176
|
-
compatibilityDate: string |
|
|
177
|
-
compatibilityFlags:
|
|
178
|
-
usageModel:
|
|
192
|
+
compatibilityDate: string | undefined;
|
|
193
|
+
compatibilityFlags: undefined | string[];
|
|
194
|
+
usageModel: undefined | "bundled" | "unbound";
|
|
195
|
+
env: string | undefined;
|
|
196
|
+
legacyEnv: boolean | undefined;
|
|
197
|
+
zone: string | undefined;
|
|
179
198
|
}) {
|
|
180
199
|
assert(props.accountId, "accountId is required");
|
|
181
200
|
assert(props.apiToken, "apiToken is required");
|
|
182
|
-
const
|
|
201
|
+
const previewToken = useWorker({
|
|
183
202
|
name: props.name,
|
|
184
203
|
bundle: props.bundle,
|
|
185
204
|
format: props.format,
|
|
@@ -187,100 +206,194 @@ function Remote(props: {
|
|
|
187
206
|
accountId: props.accountId,
|
|
188
207
|
apiToken: props.apiToken,
|
|
189
208
|
bindings: props.bindings,
|
|
190
|
-
|
|
209
|
+
assetPaths: props.assetPaths,
|
|
191
210
|
port: props.port,
|
|
192
211
|
compatibilityDate: props.compatibilityDate,
|
|
193
212
|
compatibilityFlags: props.compatibilityFlags,
|
|
194
213
|
usageModel: props.usageModel,
|
|
214
|
+
env: props.env,
|
|
215
|
+
legacyEnv: props.legacyEnv,
|
|
216
|
+
zone: props.zone,
|
|
195
217
|
});
|
|
196
218
|
|
|
197
|
-
|
|
219
|
+
usePreviewServer({
|
|
220
|
+
previewToken,
|
|
221
|
+
publicRoot: props.public,
|
|
222
|
+
port: props.port,
|
|
223
|
+
});
|
|
198
224
|
|
|
199
|
-
useInspector(
|
|
225
|
+
useInspector({
|
|
226
|
+
inspectorUrl: previewToken ? previewToken.inspectorUrl.href : undefined,
|
|
227
|
+
port: props.inspectorPort,
|
|
228
|
+
logToTerminal: true,
|
|
229
|
+
});
|
|
200
230
|
return null;
|
|
201
231
|
}
|
|
202
232
|
function Local(props: {
|
|
203
|
-
name:
|
|
204
|
-
bundle: EsbuildBundle |
|
|
205
|
-
format: CfScriptFormat;
|
|
233
|
+
name: undefined | string;
|
|
234
|
+
bundle: EsbuildBundle | undefined;
|
|
235
|
+
format: CfScriptFormat | undefined;
|
|
206
236
|
bindings: CfWorkerInit["bindings"];
|
|
207
|
-
|
|
208
|
-
|
|
237
|
+
assetPaths: undefined | AssetPaths;
|
|
238
|
+
public: undefined | string;
|
|
209
239
|
port: number;
|
|
240
|
+
rules: Config["rules"];
|
|
241
|
+
inspectorPort: number;
|
|
242
|
+
enableLocalPersistence: boolean;
|
|
210
243
|
}) {
|
|
211
244
|
const { inspectorUrl } = useLocalWorker({
|
|
212
245
|
name: props.name,
|
|
213
246
|
bundle: props.bundle,
|
|
214
247
|
format: props.format,
|
|
215
248
|
bindings: props.bindings,
|
|
249
|
+
assetPaths: props.assetPaths,
|
|
250
|
+
public: props.public,
|
|
216
251
|
port: props.port,
|
|
252
|
+
rules: props.rules,
|
|
253
|
+
inspectorPort: props.port,
|
|
254
|
+
enableLocalPersistence: props.enableLocalPersistence,
|
|
255
|
+
});
|
|
256
|
+
useInspector({
|
|
257
|
+
inspectorUrl,
|
|
258
|
+
port: props.inspectorPort,
|
|
259
|
+
logToTerminal: false,
|
|
217
260
|
});
|
|
218
|
-
useInspector(inspectorUrl);
|
|
219
261
|
return null;
|
|
220
262
|
}
|
|
221
263
|
|
|
222
264
|
function useLocalWorker(props: {
|
|
223
|
-
name:
|
|
224
|
-
bundle: EsbuildBundle |
|
|
225
|
-
|
|
265
|
+
name: undefined | string;
|
|
266
|
+
bundle: EsbuildBundle | undefined;
|
|
267
|
+
rules: Config["rules"];
|
|
268
|
+
format: CfScriptFormat | undefined;
|
|
226
269
|
bindings: CfWorkerInit["bindings"];
|
|
270
|
+
assetPaths: undefined | AssetPaths;
|
|
271
|
+
public: undefined | string;
|
|
227
272
|
port: number;
|
|
273
|
+
inspectorPort: number;
|
|
274
|
+
enableLocalPersistence: boolean;
|
|
228
275
|
}) {
|
|
229
276
|
// TODO: pass vars via command line
|
|
230
|
-
const { bundle, format, bindings, port } = props;
|
|
277
|
+
const { bundle, format, bindings, port, assetPaths, inspectorPort } = props;
|
|
231
278
|
const local = useRef<ReturnType<typeof spawn>>();
|
|
232
279
|
const removeSignalExitListener = useRef<() => void>();
|
|
233
|
-
const [inspectorUrl, setInspectorUrl] = useState<string |
|
|
280
|
+
const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
|
|
234
281
|
useEffect(() => {
|
|
235
282
|
async function startLocalWorker() {
|
|
236
|
-
if (!bundle) return;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
283
|
+
if (!bundle || !format) return;
|
|
284
|
+
|
|
285
|
+
// port for the worker
|
|
286
|
+
await waitForPortToBeAvailable(port, { retryPeriod: 200, timeout: 2000 });
|
|
287
|
+
// port for inspector
|
|
288
|
+
await waitForPortToBeAvailable(inspectorPort, {
|
|
289
|
+
retryPeriod: 200,
|
|
290
|
+
timeout: 2000,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (props.public) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
'⎔ A "public" folder is not yet supported in local mode.'
|
|
296
|
+
);
|
|
241
297
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
298
|
+
|
|
299
|
+
// In local mode, we want to copy all referenced modules into
|
|
300
|
+
// the output bundle directory before starting up
|
|
301
|
+
for (const module of bundle.modules) {
|
|
302
|
+
await writeFile(
|
|
303
|
+
path.join(path.dirname(bundle.path), module.name),
|
|
304
|
+
module.content
|
|
305
|
+
);
|
|
246
306
|
}
|
|
247
307
|
|
|
248
308
|
console.log("⎔ Starting a local server...");
|
|
249
309
|
// TODO: just use execa for this
|
|
250
|
-
local.current = spawn(
|
|
251
|
-
"
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
310
|
+
local.current = spawn(
|
|
311
|
+
"node",
|
|
312
|
+
[
|
|
313
|
+
"--experimental-vm-modules",
|
|
314
|
+
"--inspect",
|
|
315
|
+
require.resolve("miniflare/cli"),
|
|
316
|
+
bundle.path,
|
|
317
|
+
"--watch",
|
|
318
|
+
"--wrangler-config",
|
|
319
|
+
path.join(__dirname, "../miniflare-config-stubs/wrangler.empty.toml"),
|
|
320
|
+
"--env",
|
|
321
|
+
path.join(__dirname, "../miniflare-config-stubs/.env.empty"),
|
|
322
|
+
"--package",
|
|
323
|
+
path.join(__dirname, "../miniflare-config-stubs/package.empty.json"),
|
|
324
|
+
"--port",
|
|
325
|
+
port.toString(),
|
|
326
|
+
...(assetPaths
|
|
327
|
+
? [
|
|
328
|
+
"--site",
|
|
329
|
+
path.join(process.cwd(), assetPaths.baseDirectory),
|
|
330
|
+
...assetPaths.includePatterns.map((pattern) => [
|
|
331
|
+
"--site-include",
|
|
332
|
+
pattern,
|
|
333
|
+
]),
|
|
334
|
+
...assetPaths.excludePatterns.map((pattern) => [
|
|
335
|
+
"--site-exclude",
|
|
336
|
+
pattern,
|
|
337
|
+
]),
|
|
338
|
+
].flatMap((x) => x)
|
|
339
|
+
: []),
|
|
340
|
+
...(props.enableLocalPersistence
|
|
341
|
+
? ["--kv-persist", "--cache-persist", "--do-persist"]
|
|
342
|
+
: []),
|
|
343
|
+
...Object.entries(bindings.vars || {}).flatMap(([key, value]) => {
|
|
344
|
+
return ["--binding", `${key}=${value}`];
|
|
345
|
+
}),
|
|
346
|
+
...(bindings.kv_namespaces || []).flatMap(({ binding }) => {
|
|
347
|
+
return ["--kv", binding];
|
|
348
|
+
}),
|
|
349
|
+
...(bindings.durable_objects?.bindings || []).flatMap(
|
|
350
|
+
({ name, class_name }) => {
|
|
351
|
+
return ["--do", `${name}=${class_name}`];
|
|
352
|
+
}
|
|
353
|
+
),
|
|
354
|
+
...Object.entries(bindings.wasm_modules || {}).flatMap(
|
|
355
|
+
([name, filePath]) => {
|
|
356
|
+
return [
|
|
357
|
+
"--wasm",
|
|
358
|
+
`${name}=${path.join(process.cwd(), filePath)}`,
|
|
359
|
+
];
|
|
360
|
+
}
|
|
361
|
+
),
|
|
362
|
+
...bundle.modules.reduce<string[]>((cmd, { name, type }) => {
|
|
363
|
+
if (format === "service-worker") {
|
|
364
|
+
if (type === "compiled-wasm") {
|
|
365
|
+
// In service-worker format, .wasm modules are referenced
|
|
366
|
+
// by global identifiers, so we convert it here.
|
|
367
|
+
// This identifier has to be a valid JS identifier,
|
|
368
|
+
// so we replace all non alphanumeric characters
|
|
369
|
+
// with an underscore.
|
|
370
|
+
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
371
|
+
return cmd.concat([`--wasm`, `${identifier}=${name}`]);
|
|
372
|
+
} else {
|
|
373
|
+
// TODO: we should actually support this
|
|
374
|
+
throw new Error(
|
|
375
|
+
`⎔ Unsupported module type ${type} for file ${name} in service-worker format`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return cmd;
|
|
380
|
+
}, []),
|
|
381
|
+
"--modules",
|
|
382
|
+
String(format === "modules"),
|
|
383
|
+
...(props.rules || [])
|
|
384
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
385
|
+
.concat(DEFAULT_MODULE_RULES!)
|
|
386
|
+
.flatMap((rule) =>
|
|
387
|
+
rule.globs.flatMap((glob) => [
|
|
388
|
+
"--modules-rule",
|
|
389
|
+
`${rule.type}=${glob}`,
|
|
390
|
+
])
|
|
391
|
+
),
|
|
392
|
+
],
|
|
393
|
+
{
|
|
394
|
+
cwd: path.dirname(bundle.path),
|
|
395
|
+
}
|
|
396
|
+
);
|
|
284
397
|
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
285
398
|
|
|
286
399
|
local.current.on("close", (code) => {
|
|
@@ -289,14 +402,14 @@ function useLocalWorker(props: {
|
|
|
289
402
|
}
|
|
290
403
|
});
|
|
291
404
|
|
|
292
|
-
local.current.stdout
|
|
405
|
+
local.current.stdout?.on("data", (data: Buffer) => {
|
|
293
406
|
console.log(`${data.toString()}`);
|
|
294
407
|
});
|
|
295
408
|
|
|
296
|
-
local.current.stderr
|
|
409
|
+
local.current.stderr?.on("data", (data: Buffer) => {
|
|
297
410
|
console.error(`${data.toString()}`);
|
|
298
411
|
const matches =
|
|
299
|
-
/Debugger listening on (ws:\/\/127\.0\.0\.1
|
|
412
|
+
/Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
|
|
300
413
|
data.toString()
|
|
301
414
|
);
|
|
302
415
|
if (matches) {
|
|
@@ -339,14 +452,20 @@ function useLocalWorker(props: {
|
|
|
339
452
|
bundle,
|
|
340
453
|
format,
|
|
341
454
|
port,
|
|
455
|
+
inspectorPort,
|
|
342
456
|
bindings.durable_objects?.bindings,
|
|
343
457
|
bindings.kv_namespaces,
|
|
344
458
|
bindings.vars,
|
|
459
|
+
props.enableLocalPersistence,
|
|
460
|
+
assetPaths,
|
|
461
|
+
props.public,
|
|
462
|
+
props.rules,
|
|
463
|
+
bindings.wasm_modules,
|
|
345
464
|
]);
|
|
346
465
|
return { inspectorUrl };
|
|
347
466
|
}
|
|
348
467
|
|
|
349
|
-
function useTmpDir(): string |
|
|
468
|
+
function useTmpDir(): string | undefined {
|
|
350
469
|
const [directory, setDirectory] = useState<DirectoryResult>();
|
|
351
470
|
const handleError = useErrorHandler();
|
|
352
471
|
useEffect(() => {
|
|
@@ -369,7 +488,7 @@ function useTmpDir(): string | void {
|
|
|
369
488
|
return () => {
|
|
370
489
|
dir.cleanup().catch(() => {
|
|
371
490
|
// extremely unlikely,
|
|
372
|
-
// but it's
|
|
491
|
+
// but it's 2022 after all
|
|
373
492
|
console.error("failed to cleanup tmp dir");
|
|
374
493
|
});
|
|
375
494
|
};
|
|
@@ -378,36 +497,38 @@ function useTmpDir(): string | void {
|
|
|
378
497
|
}
|
|
379
498
|
|
|
380
499
|
function useCustomBuild(
|
|
381
|
-
expectedEntry:
|
|
500
|
+
expectedEntry: Entry,
|
|
382
501
|
props: {
|
|
383
502
|
command?: undefined | string;
|
|
384
503
|
cwd?: undefined | string;
|
|
385
504
|
watch_dir?: undefined | string;
|
|
386
505
|
}
|
|
387
|
-
):
|
|
388
|
-
const [entry, setEntry] = useState<
|
|
506
|
+
): undefined | Entry {
|
|
507
|
+
const [entry, setEntry] = useState<Entry | undefined>(
|
|
389
508
|
// if there's no build command, just return the expected entry
|
|
390
|
-
props.command ?
|
|
509
|
+
!props.command ? expectedEntry : undefined
|
|
391
510
|
);
|
|
392
511
|
const { command, cwd, watch_dir } = props;
|
|
393
512
|
useEffect(() => {
|
|
394
513
|
if (!command) return;
|
|
395
|
-
let cmd,
|
|
514
|
+
let cmd: ExecaChildProcess<string> | undefined,
|
|
515
|
+
interval: NodeJS.Timeout | undefined;
|
|
396
516
|
console.log("running:", command);
|
|
397
|
-
|
|
398
|
-
cmd = execa(commandPieces[0], commandPieces.slice(1), {
|
|
517
|
+
cmd = execaCommand(command, {
|
|
399
518
|
...(cwd && { cwd }),
|
|
519
|
+
shell: true,
|
|
400
520
|
stderr: "inherit",
|
|
401
521
|
stdout: "inherit",
|
|
402
522
|
});
|
|
403
523
|
if (watch_dir) {
|
|
404
524
|
watch(watch_dir, { persistent: true, ignoreInitial: true }).on(
|
|
405
525
|
"all",
|
|
406
|
-
(_event,
|
|
407
|
-
console.log(`The file ${
|
|
408
|
-
cmd
|
|
409
|
-
cmd =
|
|
526
|
+
(_event, filePath) => {
|
|
527
|
+
console.log(`The file ${filePath} changed, restarting build...`);
|
|
528
|
+
cmd?.kill();
|
|
529
|
+
cmd = execaCommand(command, {
|
|
410
530
|
...(cwd && { cwd }),
|
|
531
|
+
shell: true,
|
|
411
532
|
stderr: "inherit",
|
|
412
533
|
stdout: "inherit",
|
|
413
534
|
});
|
|
@@ -419,27 +540,39 @@ function useCustomBuild(
|
|
|
419
540
|
// if it does, we're done
|
|
420
541
|
const startedAt = Date.now();
|
|
421
542
|
interval = setInterval(() => {
|
|
422
|
-
|
|
423
|
-
|
|
543
|
+
let fileExists = false;
|
|
544
|
+
try {
|
|
545
|
+
// Use require.resolve to use node's resolution algorithm,
|
|
546
|
+
// this lets us use paths without explicit .js extension
|
|
547
|
+
// TODO: we should probably remove this, because it doesn't
|
|
548
|
+
// take into consideration other extensions like .tsx, .ts, .jsx, etc
|
|
549
|
+
fileExists = existsSync(require.resolve(expectedEntry.file));
|
|
550
|
+
} catch (e) {
|
|
551
|
+
// fail silently, usually means require.resolve threw MODULE_NOT_FOUND
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (fileExists === true) {
|
|
555
|
+
interval && clearInterval(interval);
|
|
424
556
|
setEntry(expectedEntry);
|
|
425
557
|
} else {
|
|
426
558
|
const elapsed = Date.now() - startedAt;
|
|
427
559
|
// timeout after 30 seconds of waiting
|
|
428
|
-
if (elapsed > 1000 *
|
|
429
|
-
console.error(
|
|
430
|
-
|
|
431
|
-
|
|
560
|
+
if (elapsed > 1000 * 30) {
|
|
561
|
+
console.error(
|
|
562
|
+
`⎔ Build timed out, Could not resolve ${expectedEntry.file}`
|
|
563
|
+
);
|
|
564
|
+
interval && clearInterval(interval);
|
|
565
|
+
cmd?.kill();
|
|
432
566
|
}
|
|
433
567
|
}
|
|
434
568
|
}, 200);
|
|
435
|
-
// TODO: we could probably timeout here after a while
|
|
436
569
|
|
|
437
570
|
return () => {
|
|
438
571
|
if (cmd) {
|
|
439
572
|
cmd.kill();
|
|
440
573
|
cmd = undefined;
|
|
441
574
|
}
|
|
442
|
-
clearInterval(interval);
|
|
575
|
+
interval && clearInterval(interval);
|
|
443
576
|
interval = undefined;
|
|
444
577
|
};
|
|
445
578
|
}, [command, cwd, expectedEntry, watch_dir]);
|
|
@@ -449,97 +582,115 @@ function useCustomBuild(
|
|
|
449
582
|
type EsbuildBundle = {
|
|
450
583
|
id: number;
|
|
451
584
|
path: string;
|
|
452
|
-
entry:
|
|
585
|
+
entry: Entry;
|
|
453
586
|
type: "esm" | "commonjs";
|
|
454
|
-
exports: string[];
|
|
455
587
|
modules: CfModule[];
|
|
588
|
+
serveAssetsFromWorker: boolean;
|
|
456
589
|
};
|
|
457
590
|
|
|
458
|
-
function useEsbuild(
|
|
459
|
-
entry
|
|
460
|
-
destination
|
|
461
|
-
staticRoot
|
|
462
|
-
jsxFactory
|
|
463
|
-
jsxFragment
|
|
464
|
-
|
|
465
|
-
|
|
591
|
+
function useEsbuild({
|
|
592
|
+
entry,
|
|
593
|
+
destination,
|
|
594
|
+
staticRoot,
|
|
595
|
+
jsxFactory,
|
|
596
|
+
jsxFragment,
|
|
597
|
+
format,
|
|
598
|
+
rules,
|
|
599
|
+
serveAssetsFromWorker,
|
|
600
|
+
}: {
|
|
601
|
+
entry: undefined | Entry;
|
|
602
|
+
destination: string | undefined;
|
|
603
|
+
format: CfScriptFormat | undefined;
|
|
604
|
+
staticRoot: undefined | string;
|
|
605
|
+
jsxFactory: string | undefined;
|
|
606
|
+
jsxFragment: string | undefined;
|
|
607
|
+
rules: Config["rules"];
|
|
608
|
+
serveAssetsFromWorker: boolean;
|
|
609
|
+
}): EsbuildBundle | undefined {
|
|
466
610
|
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
467
611
|
useEffect(() => {
|
|
468
|
-
let
|
|
612
|
+
let stopWatching: (() => void) | undefined = undefined;
|
|
613
|
+
|
|
614
|
+
const watchMode: WatchMode = {
|
|
615
|
+
async onRebuild(error) {
|
|
616
|
+
if (error) console.error("watch build failed:", error);
|
|
617
|
+
else {
|
|
618
|
+
// nothing really changes here, so let's increment the id
|
|
619
|
+
// to change the return object's identity
|
|
620
|
+
setBundle((previousBundle) => {
|
|
621
|
+
assert(
|
|
622
|
+
previousBundle,
|
|
623
|
+
"Rebuild triggered with no previous build available"
|
|
624
|
+
);
|
|
625
|
+
return { ...previousBundle, id: previousBundle.id + 1 };
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
|
|
469
631
|
async function build() {
|
|
470
|
-
if (!destination || !entry) return;
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
...(jsxFactory && { jsxFactory }),
|
|
483
|
-
...(jsxFragment && { jsxFragment }),
|
|
484
|
-
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
485
|
-
conditions: ["worker", "browser"],
|
|
486
|
-
plugins: [moduleCollector.plugin],
|
|
487
|
-
// TODO: import.meta.url
|
|
488
|
-
watch: {
|
|
489
|
-
async onRebuild(error) {
|
|
490
|
-
if (error) console.error("watch build failed:", error);
|
|
491
|
-
else {
|
|
492
|
-
// nothing really changes here, so let's increment the id
|
|
493
|
-
// to change the return object's identity
|
|
494
|
-
setBundle((previousBundle) => ({
|
|
495
|
-
...previousBundle,
|
|
496
|
-
id: previousBundle.id + 1,
|
|
497
|
-
}));
|
|
498
|
-
}
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
});
|
|
632
|
+
if (!destination || !entry || !format) return;
|
|
633
|
+
|
|
634
|
+
const { resolvedEntryPointPath, bundleType, modules, stop } =
|
|
635
|
+
await bundleWorker(entry, destination, {
|
|
636
|
+
// In dev, we serve assets from the local proxy before we send the request to the worker.
|
|
637
|
+
serveAssetsFromWorker: false,
|
|
638
|
+
jsxFactory,
|
|
639
|
+
jsxFragment,
|
|
640
|
+
format,
|
|
641
|
+
rules,
|
|
642
|
+
watch: watchMode,
|
|
643
|
+
});
|
|
502
644
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
); // assumedly only one entry point
|
|
645
|
+
// Capture the `stop()` method to use as the `useEffect()` destructor.
|
|
646
|
+
stopWatching = stop;
|
|
506
647
|
|
|
507
648
|
setBundle({
|
|
508
649
|
id: 0,
|
|
509
650
|
entry,
|
|
510
|
-
path:
|
|
511
|
-
type:
|
|
512
|
-
|
|
513
|
-
|
|
651
|
+
path: resolvedEntryPointPath,
|
|
652
|
+
type: bundleType,
|
|
653
|
+
modules,
|
|
654
|
+
serveAssetsFromWorker,
|
|
514
655
|
});
|
|
515
656
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
// and we don't want to end the process
|
|
519
|
-
// on build errors anyway
|
|
520
|
-
// so this is a no-op error handler
|
|
657
|
+
|
|
658
|
+
build().catch(() => {
|
|
659
|
+
// esbuild already logs errors to stderr and we don't want to end the process
|
|
660
|
+
// on build errors anyway so this is a no-op error handler
|
|
521
661
|
});
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
662
|
+
|
|
663
|
+
return stopWatching;
|
|
664
|
+
}, [
|
|
665
|
+
entry,
|
|
666
|
+
destination,
|
|
667
|
+
staticRoot,
|
|
668
|
+
jsxFactory,
|
|
669
|
+
jsxFragment,
|
|
670
|
+
format,
|
|
671
|
+
serveAssetsFromWorker,
|
|
672
|
+
rules,
|
|
673
|
+
]);
|
|
526
674
|
return bundle;
|
|
527
675
|
}
|
|
528
676
|
|
|
529
677
|
function useWorker(props: {
|
|
530
|
-
name:
|
|
531
|
-
bundle: EsbuildBundle |
|
|
532
|
-
format: CfScriptFormat;
|
|
678
|
+
name: undefined | string;
|
|
679
|
+
bundle: EsbuildBundle | undefined;
|
|
680
|
+
format: CfScriptFormat | undefined;
|
|
533
681
|
modules: CfModule[];
|
|
534
682
|
accountId: string;
|
|
535
683
|
apiToken: string;
|
|
536
684
|
bindings: CfWorkerInit["bindings"];
|
|
537
|
-
|
|
685
|
+
assetPaths: undefined | AssetPaths;
|
|
538
686
|
port: number;
|
|
539
|
-
compatibilityDate: string |
|
|
540
|
-
compatibilityFlags: string[] |
|
|
541
|
-
usageModel:
|
|
542
|
-
|
|
687
|
+
compatibilityDate: string | undefined;
|
|
688
|
+
compatibilityFlags: string[] | undefined;
|
|
689
|
+
usageModel: undefined | "bundled" | "unbound";
|
|
690
|
+
env: string | undefined;
|
|
691
|
+
legacyEnv: boolean | undefined;
|
|
692
|
+
zone: string | undefined;
|
|
693
|
+
}): CfPreviewToken | undefined {
|
|
543
694
|
const {
|
|
544
695
|
name,
|
|
545
696
|
bundle,
|
|
@@ -548,13 +699,13 @@ function useWorker(props: {
|
|
|
548
699
|
accountId,
|
|
549
700
|
apiToken,
|
|
550
701
|
bindings,
|
|
551
|
-
|
|
702
|
+
assetPaths,
|
|
552
703
|
compatibilityDate,
|
|
553
704
|
compatibilityFlags,
|
|
554
705
|
usageModel,
|
|
555
706
|
port,
|
|
556
707
|
} = props;
|
|
557
|
-
const [token, setToken] = useState<CfPreviewToken>();
|
|
708
|
+
const [token, setToken] = useState<CfPreviewToken | undefined>();
|
|
558
709
|
|
|
559
710
|
// This is the most reliable way to detect whether
|
|
560
711
|
// something's "happened" in our system; We make a ref and
|
|
@@ -563,44 +714,34 @@ function useWorker(props: {
|
|
|
563
714
|
|
|
564
715
|
useEffect(() => {
|
|
565
716
|
async function start() {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
// TODO: a much better error message here, with what to do next
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
if (format === "service-worker" && bundle.type !== "esm") {
|
|
573
|
-
console.error("⎔ Cannot use service-worker with a esm bundle.");
|
|
574
|
-
// TODO: a much better error message here, with what to do next
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
717
|
+
setToken(undefined); // reset token in case we're re-running
|
|
718
|
+
|
|
719
|
+
if (!bundle || !format) return;
|
|
577
720
|
|
|
578
721
|
if (!startedRef.current) {
|
|
579
|
-
console.log("⎔ Starting server...");
|
|
580
722
|
startedRef.current = true;
|
|
581
723
|
} else {
|
|
582
|
-
console.log("⎔ Detected changes,
|
|
724
|
+
console.log("⎔ Detected changes, restarted server.");
|
|
583
725
|
}
|
|
584
726
|
|
|
585
|
-
const assets =
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
namespace: undefined,
|
|
596
|
-
}; // TODO: cancellable?
|
|
727
|
+
const assets = await syncAssets(
|
|
728
|
+
accountId,
|
|
729
|
+
// When we're using the newer service environments, we wouldn't
|
|
730
|
+
// have added the env name on to the script name. However, we must
|
|
731
|
+
// include it in the kv namespace name regardless (since there's no
|
|
732
|
+
// concept of service environments for kv namespaces yet).
|
|
733
|
+
name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
|
|
734
|
+
assetPaths,
|
|
735
|
+
true
|
|
736
|
+
); // TODO: cancellable?
|
|
597
737
|
|
|
598
738
|
const content = await readFile(bundle.path, "utf-8");
|
|
739
|
+
|
|
599
740
|
const init: CfWorkerInit = {
|
|
600
741
|
name,
|
|
601
742
|
main: {
|
|
602
743
|
name: path.basename(bundle.path),
|
|
603
|
-
type: format
|
|
744
|
+
type: format === "modules" ? "esm" : "commonjs",
|
|
604
745
|
content,
|
|
605
746
|
},
|
|
606
747
|
modules: modules.concat(
|
|
@@ -619,6 +760,13 @@ function useWorker(props: {
|
|
|
619
760
|
? { binding: "__STATIC_CONTENT", id: assets.namespace }
|
|
620
761
|
: []
|
|
621
762
|
),
|
|
763
|
+
text_blobs: {
|
|
764
|
+
...bindings.text_blobs,
|
|
765
|
+
...(assets.manifest &&
|
|
766
|
+
format === "service-worker" && {
|
|
767
|
+
__STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
|
|
768
|
+
}),
|
|
769
|
+
},
|
|
622
770
|
},
|
|
623
771
|
migrations: undefined, // no migrations in dev
|
|
624
772
|
compatibility_date: compatibilityDate,
|
|
@@ -626,10 +774,14 @@ function useWorker(props: {
|
|
|
626
774
|
usage_model: usageModel,
|
|
627
775
|
};
|
|
628
776
|
setToken(
|
|
629
|
-
await createWorker(
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
777
|
+
await createWorker(
|
|
778
|
+
init,
|
|
779
|
+
{
|
|
780
|
+
accountId,
|
|
781
|
+
apiToken,
|
|
782
|
+
},
|
|
783
|
+
{ env: props.env, legacyEnv: props.legacyEnv, zone: props.zone }
|
|
784
|
+
)
|
|
633
785
|
);
|
|
634
786
|
}
|
|
635
787
|
start().catch((err) => {
|
|
@@ -644,95 +796,19 @@ function useWorker(props: {
|
|
|
644
796
|
accountId,
|
|
645
797
|
apiToken,
|
|
646
798
|
port,
|
|
647
|
-
|
|
799
|
+
assetPaths,
|
|
648
800
|
compatibilityDate,
|
|
649
801
|
compatibilityFlags,
|
|
650
802
|
usageModel,
|
|
651
803
|
bindings,
|
|
652
804
|
modules,
|
|
805
|
+
props.env,
|
|
806
|
+
props.legacyEnv,
|
|
807
|
+
props.zone,
|
|
653
808
|
]);
|
|
654
809
|
return token;
|
|
655
810
|
}
|
|
656
811
|
|
|
657
|
-
function useProxy({
|
|
658
|
-
token,
|
|
659
|
-
publicRoot,
|
|
660
|
-
port,
|
|
661
|
-
}: {
|
|
662
|
-
token: CfPreviewToken | void;
|
|
663
|
-
publicRoot: void | string;
|
|
664
|
-
port: number;
|
|
665
|
-
}) {
|
|
666
|
-
useEffect(() => {
|
|
667
|
-
if (!token) return;
|
|
668
|
-
// TODO(soon): since headers are added in callbacks, the server
|
|
669
|
-
// does not need to restart when changes are made.
|
|
670
|
-
const host = token.host;
|
|
671
|
-
const proxy = createHttpProxy({
|
|
672
|
-
host,
|
|
673
|
-
assetPath: typeof publicRoot === "string" ? publicRoot : null,
|
|
674
|
-
onRequest: (headers) => {
|
|
675
|
-
headers["cf-workers-preview-token"] = token.value;
|
|
676
|
-
},
|
|
677
|
-
onResponse: (headers) => {
|
|
678
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
679
|
-
// Rewrite the remote host to the local host.
|
|
680
|
-
if (typeof value === "string" && value.includes(host)) {
|
|
681
|
-
headers[name] = value
|
|
682
|
-
.replaceAll(`https://${host}`, `http://localhost:${port}`)
|
|
683
|
-
.replaceAll(host, `localhost:${port}`);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
},
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
690
|
-
|
|
691
|
-
const server = proxy.listen(port);
|
|
692
|
-
|
|
693
|
-
// TODO(soon): refactor logging format into its own function
|
|
694
|
-
proxy.on("request", function (req, res) {
|
|
695
|
-
// log all requests
|
|
696
|
-
console.log(
|
|
697
|
-
new Date().toLocaleTimeString(),
|
|
698
|
-
req.method,
|
|
699
|
-
req.url,
|
|
700
|
-
res.statusCode
|
|
701
|
-
);
|
|
702
|
-
});
|
|
703
|
-
proxy.on("upgrade", (req) => {
|
|
704
|
-
console.log(
|
|
705
|
-
new Date().toLocaleTimeString(),
|
|
706
|
-
req.method,
|
|
707
|
-
req.url,
|
|
708
|
-
101,
|
|
709
|
-
"(WebSocket)"
|
|
710
|
-
);
|
|
711
|
-
});
|
|
712
|
-
proxy.on("error", (err) => {
|
|
713
|
-
console.error(new Date().toLocaleTimeString(), err);
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
return () => {
|
|
717
|
-
proxy.close();
|
|
718
|
-
server.close();
|
|
719
|
-
};
|
|
720
|
-
}, [token, publicRoot, port]);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function useInspector(inspectorUrl: string | void) {
|
|
724
|
-
useEffect(() => {
|
|
725
|
-
if (!inspectorUrl) return;
|
|
726
|
-
|
|
727
|
-
const inspector = new DtInspector(inspectorUrl);
|
|
728
|
-
const abortController = inspector.proxyTo(9229);
|
|
729
|
-
return () => {
|
|
730
|
-
inspector.close();
|
|
731
|
-
abortController.abort();
|
|
732
|
-
};
|
|
733
|
-
}, [inspectorUrl]);
|
|
734
|
-
}
|
|
735
|
-
|
|
736
812
|
function sleep(period: number) {
|
|
737
813
|
return new Promise((resolve) => setTimeout(resolve, period));
|
|
738
814
|
}
|
|
@@ -740,7 +816,7 @@ const SLEEP_DURATION = 2000;
|
|
|
740
816
|
// really need a first class api for this
|
|
741
817
|
const hostNameRegex = /userHostname="(.*)"/g;
|
|
742
818
|
async function findTunnelHostname() {
|
|
743
|
-
let hostName: string;
|
|
819
|
+
let hostName: string | undefined;
|
|
744
820
|
while (!hostName) {
|
|
745
821
|
try {
|
|
746
822
|
const resp = await fetch("http://localhost:8789/metrics");
|
|
@@ -766,7 +842,7 @@ function useTunnel(toggle: boolean) {
|
|
|
766
842
|
await commandExists("cloudflared");
|
|
767
843
|
} catch (e) {
|
|
768
844
|
console.error(
|
|
769
|
-
"To share your worker on the
|
|
845
|
+
"To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
|
|
770
846
|
);
|
|
771
847
|
return;
|
|
772
848
|
}
|
|
@@ -817,38 +893,49 @@ type useHotkeysInitialState = {
|
|
|
817
893
|
local: boolean;
|
|
818
894
|
tunnel: boolean;
|
|
819
895
|
};
|
|
820
|
-
function useHotkeys(
|
|
896
|
+
function useHotkeys(
|
|
897
|
+
initial: useHotkeysInitialState,
|
|
898
|
+
port: number,
|
|
899
|
+
inspectorPort: number
|
|
900
|
+
) {
|
|
821
901
|
// UGH, we should put port in context instead
|
|
822
902
|
const [toggles, setToggles] = useState(initial);
|
|
823
903
|
useInput(
|
|
824
904
|
async (
|
|
825
905
|
input,
|
|
826
|
-
// eslint-disable-next-line
|
|
906
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
827
907
|
key
|
|
828
908
|
) => {
|
|
829
|
-
switch (input) {
|
|
830
|
-
|
|
831
|
-
|
|
909
|
+
switch (input.toLowerCase()) {
|
|
910
|
+
// open browser
|
|
911
|
+
case "b": {
|
|
912
|
+
await openInBrowser(`http://localhost:${port}`);
|
|
832
913
|
break;
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
914
|
+
}
|
|
915
|
+
// toggle inspector
|
|
916
|
+
case "d": {
|
|
917
|
+
await openInBrowser(
|
|
918
|
+
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`
|
|
836
919
|
);
|
|
837
920
|
break;
|
|
838
|
-
|
|
921
|
+
}
|
|
922
|
+
// toggle tunnel
|
|
923
|
+
case "s":
|
|
839
924
|
setToggles((previousToggles) => ({
|
|
840
925
|
...previousToggles,
|
|
841
926
|
tunnel: !previousToggles.tunnel,
|
|
842
927
|
}));
|
|
843
928
|
break;
|
|
844
|
-
|
|
929
|
+
// toggle local
|
|
930
|
+
case "l":
|
|
845
931
|
setToggles((previousToggles) => ({
|
|
846
932
|
...previousToggles,
|
|
847
933
|
local: !previousToggles.local,
|
|
848
934
|
}));
|
|
849
935
|
break;
|
|
850
|
-
|
|
851
|
-
case "
|
|
936
|
+
// shut down
|
|
937
|
+
case "q":
|
|
938
|
+
case "x":
|
|
852
939
|
process.exit(0);
|
|
853
940
|
break;
|
|
854
941
|
default:
|
|
@@ -866,7 +953,7 @@ function ErrorFallback(props: { error: Error }) {
|
|
|
866
953
|
return (
|
|
867
954
|
<>
|
|
868
955
|
<Text>Something went wrong:</Text>
|
|
869
|
-
<Text>{props.error.
|
|
956
|
+
<Text>{props.error.stack}</Text>
|
|
870
957
|
</>
|
|
871
958
|
);
|
|
872
959
|
}
|