wrangler 0.0.0-ece06ea → 0.0.0-ecef68635
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/README.md +50 -15
- package/bin/wrangler.js +94 -31
- package/config-schema.json +3074 -0
- package/kv-asset-handler.js +1 -0
- package/package.json +155 -75
- package/templates/__tests__/pages-dev-util.test.ts +128 -0
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/checked-fetch.js +30 -0
- package/templates/facade.d.ts +19 -0
- package/templates/gitignore +170 -0
- package/templates/init-tests/test-jest-new-worker.js +23 -0
- package/templates/init-tests/test-vitest-new-worker.js +24 -0
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/common.ts +67 -0
- package/templates/middleware/loader-modules.ts +134 -0
- package/templates/middleware/loader-sw.ts +229 -0
- package/templates/middleware/middleware-ensure-req-body-drained.ts +18 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
- package/templates/middleware/middleware-mock-analytics-engine.d.ts +3 -0
- package/templates/middleware/middleware-mock-analytics-engine.ts +30 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +29 -0
- package/templates/middleware/middleware-serve-static-assets.d.ts +6 -0
- package/templates/middleware/middleware-serve-static-assets.ts +56 -0
- package/templates/modules-watch-stub.js +4 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +33 -0
- package/templates/no-op-worker.js +10 -0
- package/templates/pages-dev-pipeline.ts +32 -0
- package/templates/pages-dev-util.ts +55 -0
- package/templates/pages-shim.ts +9 -0
- package/templates/pages-template-plugin.ts +190 -0
- package/templates/pages-template-worker.ts +198 -0
- package/templates/startDevWorker/InspectorProxyWorker.ts +664 -0
- package/templates/startDevWorker/ProxyWorker.ts +336 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +22 -0
- package/templates/tsconfig.json +8 -0
- package/wrangler-dist/InspectorProxyWorker.js +464 -0
- package/wrangler-dist/InspectorProxyWorker.js.map +6 -0
- package/wrangler-dist/ProxyWorker.js +241 -0
- package/wrangler-dist/ProxyWorker.js.map +6 -0
- package/wrangler-dist/cli.d.ts +26463 -0
- package/wrangler-dist/cli.js +206335 -125492
- package/wrangler-dist/wasm-sync.wasm +0 -0
- package/import_meta_url.js +0 -3
- package/miniflare-config-stubs/.env.empty +0 -0
- package/miniflare-config-stubs/package.empty.json +0 -1
- package/miniflare-config-stubs/wrangler.empty.toml +0 -0
- package/src/__tests__/clipboardy-mock.js +0 -4
- package/src/__tests__/fixtures/init/.gitkeep +0 -0
- package/src/__tests__/index.test.ts +0 -153
- package/src/__tests__/mock-cfetch.js +0 -46
- package/src/api/form_data.ts +0 -158
- package/src/api/inspect.ts +0 -441
- package/src/api/preview.ts +0 -123
- package/src/api/worker.ts +0 -161
- package/src/cfetch.ts +0 -72
- package/src/cli.ts +0 -10
- package/src/config.ts +0 -124
- package/src/dev.tsx +0 -736
- package/src/dialogs.tsx +0 -85
- package/src/index.tsx +0 -1845
- package/src/kv.tsx +0 -211
- package/src/pages.tsx +0 -344
- package/src/publish.ts +0 -354
- package/src/sites.tsx +0 -115
- package/src/tail.tsx +0 -71
- package/src/user.tsx +0 -1029
- package/src/util/fetch.ts +0 -74
- package/static-asset-facade.js +0 -47
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
- package/wrangler-dist/cli.js.map +0 -7
package/src/dev.tsx
DELETED
|
@@ -1,736 +0,0 @@
|
|
|
1
|
-
import esbuild from "esbuild";
|
|
2
|
-
import httpProxy from "http-proxy";
|
|
3
|
-
import { readFile } from "fs/promises";
|
|
4
|
-
import type { DirectoryResult } from "tmp-promise";
|
|
5
|
-
import tmp from "tmp-promise";
|
|
6
|
-
import type { CfPreviewToken } from "./api/preview";
|
|
7
|
-
import { Box, Text, useInput } from "ink";
|
|
8
|
-
import React, { useState, useEffect, useRef } from "react";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import open from "open";
|
|
11
|
-
import { DtInspector } from "./api/inspect";
|
|
12
|
-
import type { CfModule, CfVariable } from "./api/worker";
|
|
13
|
-
import { createWorker } from "./api/worker";
|
|
14
|
-
import type { CfWorkerInit } from "./api/worker";
|
|
15
|
-
import { spawn } from "child_process";
|
|
16
|
-
import onExit from "signal-exit";
|
|
17
|
-
import { syncAssets } from "./sites";
|
|
18
|
-
import clipboardy from "clipboardy";
|
|
19
|
-
import http from "node:http";
|
|
20
|
-
import serveStatic from "serve-static";
|
|
21
|
-
import commandExists from "command-exists";
|
|
22
|
-
import assert from "assert";
|
|
23
|
-
import { getAPIToken } from "./user";
|
|
24
|
-
import fetch from "node-fetch";
|
|
25
|
-
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
|
|
26
|
-
import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
|
|
27
|
-
|
|
28
|
-
type CfScriptFormat = void | "modules" | "service-worker";
|
|
29
|
-
|
|
30
|
-
type Props = {
|
|
31
|
-
name?: string;
|
|
32
|
-
entry: string;
|
|
33
|
-
port?: number;
|
|
34
|
-
format: CfScriptFormat;
|
|
35
|
-
accountId: void | string;
|
|
36
|
-
initialMode: "local" | "remote";
|
|
37
|
-
jsxFactory: void | string;
|
|
38
|
-
jsxFragment: void | string;
|
|
39
|
-
variables: { [name: string]: CfVariable };
|
|
40
|
-
public: void | string;
|
|
41
|
-
site: void | string;
|
|
42
|
-
compatibilityDate: void | string;
|
|
43
|
-
compatibilityFlags: void | string[];
|
|
44
|
-
usageModel: void | "bundled" | "unbound";
|
|
45
|
-
polyfillNode: void | boolean;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export function Dev(props: Props): JSX.Element {
|
|
49
|
-
if (props.public && props.format === "service-worker") {
|
|
50
|
-
throw new Error(
|
|
51
|
-
"You cannot use the service worker format with a `public` directory."
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
const port = props.port || 8787;
|
|
55
|
-
const apiToken = getAPIToken();
|
|
56
|
-
const directory = useTmpDir();
|
|
57
|
-
|
|
58
|
-
const bundle = useEsbuild({
|
|
59
|
-
entry: props.entry,
|
|
60
|
-
destination: directory,
|
|
61
|
-
staticRoot: props.public,
|
|
62
|
-
jsxFactory: props.jsxFactory,
|
|
63
|
-
jsxFragment: props.jsxFragment,
|
|
64
|
-
polyfillNode: props.polyfillNode,
|
|
65
|
-
});
|
|
66
|
-
if (bundle && bundle.type === "commonjs" && !props.format && props.public) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
"You cannot use the service worker format with a `public` directory."
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// @ts-expect-error whack
|
|
73
|
-
useDevtoolsRefresh(bundle?.id ?? 0);
|
|
74
|
-
|
|
75
|
-
const toggles = useHotkeys(
|
|
76
|
-
{
|
|
77
|
-
local: props.initialMode === "local",
|
|
78
|
-
tunnel: false,
|
|
79
|
-
},
|
|
80
|
-
port
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
useTunnel(toggles.tunnel);
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<>
|
|
87
|
-
{toggles.local ? (
|
|
88
|
-
<Local
|
|
89
|
-
name={props.name}
|
|
90
|
-
bundle={bundle}
|
|
91
|
-
format={props.format}
|
|
92
|
-
variables={props.variables}
|
|
93
|
-
site={props.site}
|
|
94
|
-
public={props.public}
|
|
95
|
-
port={props.port}
|
|
96
|
-
/>
|
|
97
|
-
) : (
|
|
98
|
-
<Remote
|
|
99
|
-
name={props.name}
|
|
100
|
-
bundle={bundle}
|
|
101
|
-
format={props.format}
|
|
102
|
-
accountId={props.accountId}
|
|
103
|
-
apiToken={apiToken}
|
|
104
|
-
variables={props.variables}
|
|
105
|
-
site={props.site}
|
|
106
|
-
public={props.public}
|
|
107
|
-
port={props.port}
|
|
108
|
-
compatibilityDate={props.compatibilityDate}
|
|
109
|
-
compatibilityFlags={props.compatibilityFlags}
|
|
110
|
-
usageModel={props.usageModel}
|
|
111
|
-
/>
|
|
112
|
-
)}
|
|
113
|
-
<Box borderStyle="round" paddingLeft={1} paddingRight={1}>
|
|
114
|
-
<Text>
|
|
115
|
-
{`B to open a browser, D to open Devtools, S to ${
|
|
116
|
-
toggles.tunnel ? "turn off" : "turn on"
|
|
117
|
-
} (experimental) sharing, L to ${
|
|
118
|
-
toggles.local ? "turn off" : "turn on"
|
|
119
|
-
} local mode, X to exit`}
|
|
120
|
-
</Text>
|
|
121
|
-
</Box>
|
|
122
|
-
</>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function useDevtoolsRefresh(bundleId: number) {
|
|
127
|
-
// TODO: this is a hack while we figure out
|
|
128
|
-
// a better cleaner solution to get devtools to reconnect
|
|
129
|
-
// without having to do a full refresh
|
|
130
|
-
const ref = useRef();
|
|
131
|
-
// @ts-expect-error whack
|
|
132
|
-
ref.current = bundleId;
|
|
133
|
-
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
const server = http.createServer((req, res) => {
|
|
136
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
137
|
-
res.setHeader("Access-Control-Request-Method", "*");
|
|
138
|
-
res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET");
|
|
139
|
-
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
140
|
-
if (req.method === "OPTIONS") {
|
|
141
|
-
res.writeHead(200);
|
|
142
|
-
res.end();
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
146
|
-
res.end(JSON.stringify({ value: ref.current }));
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
server.listen(3142);
|
|
150
|
-
return () => {
|
|
151
|
-
server.close();
|
|
152
|
-
};
|
|
153
|
-
}, []);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function Remote(props: {
|
|
157
|
-
name: void | string;
|
|
158
|
-
bundle: EsbuildBundle | void;
|
|
159
|
-
format: CfScriptFormat;
|
|
160
|
-
public: void | string;
|
|
161
|
-
site: void | string;
|
|
162
|
-
port: number;
|
|
163
|
-
accountId: void | string;
|
|
164
|
-
apiToken: void | string;
|
|
165
|
-
variables: { [name: string]: CfVariable };
|
|
166
|
-
compatibilityDate: string | void;
|
|
167
|
-
compatibilityFlags: void | string[];
|
|
168
|
-
usageModel: void | "bundled" | "unbound";
|
|
169
|
-
}) {
|
|
170
|
-
assert(props.accountId, "accountId is required");
|
|
171
|
-
assert(props.apiToken, "apiToken is required");
|
|
172
|
-
const token = useWorker({
|
|
173
|
-
name: props.name,
|
|
174
|
-
bundle: props.bundle,
|
|
175
|
-
format: props.format,
|
|
176
|
-
modules: [],
|
|
177
|
-
accountId: props.accountId,
|
|
178
|
-
apiToken: props.apiToken,
|
|
179
|
-
variables: props.variables,
|
|
180
|
-
sitesFolder: props.site,
|
|
181
|
-
port: props.port,
|
|
182
|
-
compatibilityDate: props.compatibilityDate,
|
|
183
|
-
compatibilityFlags: props.compatibilityFlags,
|
|
184
|
-
usageModel: props.usageModel,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
useProxy({ token, publicRoot: props.public, port: props.port });
|
|
188
|
-
|
|
189
|
-
useInspector(token ? token.inspectorUrl.href : undefined);
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
function Local(props: {
|
|
193
|
-
name: void | string;
|
|
194
|
-
bundle: EsbuildBundle | void;
|
|
195
|
-
format: CfScriptFormat;
|
|
196
|
-
variables: { [name: string]: CfVariable };
|
|
197
|
-
public: void | string;
|
|
198
|
-
site: void | string;
|
|
199
|
-
port: number;
|
|
200
|
-
}) {
|
|
201
|
-
const { inspectorUrl } = useLocalWorker({
|
|
202
|
-
name: props.name,
|
|
203
|
-
bundle: props.bundle,
|
|
204
|
-
format: props.format,
|
|
205
|
-
variables: props.variables,
|
|
206
|
-
port: props.port,
|
|
207
|
-
});
|
|
208
|
-
useInspector(inspectorUrl);
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function useLocalWorker(props: {
|
|
213
|
-
name: void | string;
|
|
214
|
-
bundle: EsbuildBundle | void;
|
|
215
|
-
format: CfScriptFormat;
|
|
216
|
-
variables: { [name: string]: CfVariable };
|
|
217
|
-
port: number;
|
|
218
|
-
}) {
|
|
219
|
-
// TODO: pass vars via command line
|
|
220
|
-
const { bundle, format, variables, port } = props;
|
|
221
|
-
const local = useRef<ReturnType<typeof spawn>>();
|
|
222
|
-
const removeSignalExitListener = useRef<() => void>();
|
|
223
|
-
const [inspectorUrl, setInspectorUrl] = useState<string | void>();
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
async function startLocalWorker() {
|
|
226
|
-
if (!bundle) return;
|
|
227
|
-
if (format === "modules" && bundle.type === "commonjs") {
|
|
228
|
-
console.error("⎔ Cannot use modules with a commonjs bundle.");
|
|
229
|
-
// TODO: a much better error message here, with what to do next
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if (format === "service-worker" && bundle.type !== "esm") {
|
|
233
|
-
console.error("⎔ Cannot use service-worker with a esm bundle.");
|
|
234
|
-
// TODO: a much better error message here, with what to do next
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
console.log("⎔ Starting a local server...");
|
|
239
|
-
local.current = spawn("node", [
|
|
240
|
-
"--experimental-vm-modules",
|
|
241
|
-
"--inspect",
|
|
242
|
-
require.resolve("miniflare/cli"),
|
|
243
|
-
bundle.path,
|
|
244
|
-
"--watch",
|
|
245
|
-
"--wrangler-config",
|
|
246
|
-
path.join(__dirname, "../miniflare-config-stubs/wrangler.empty.toml"),
|
|
247
|
-
"--env",
|
|
248
|
-
path.join(__dirname, "../miniflare-config-stubs/.env.empty"),
|
|
249
|
-
"--package",
|
|
250
|
-
path.join(__dirname, "../miniflare-config-stubs/package.empty.json"),
|
|
251
|
-
"--port",
|
|
252
|
-
port.toString(),
|
|
253
|
-
"--kv-persist",
|
|
254
|
-
"--cache-persist",
|
|
255
|
-
"--do-persist",
|
|
256
|
-
...Object.entries(variables)
|
|
257
|
-
.map(([varKey, varVal]) => {
|
|
258
|
-
if (typeof varVal === "string") {
|
|
259
|
-
return `--binding ${varKey}=${varVal}`;
|
|
260
|
-
} else if (
|
|
261
|
-
"namespaceId" in varVal &&
|
|
262
|
-
typeof varVal.namespaceId === "string"
|
|
263
|
-
) {
|
|
264
|
-
return `--kv ${varKey}`;
|
|
265
|
-
} else if ("class_name" in varVal) {
|
|
266
|
-
return `--do ${varKey}=${varVal.class_name}`;
|
|
267
|
-
}
|
|
268
|
-
})
|
|
269
|
-
.filter(Boolean),
|
|
270
|
-
"--modules",
|
|
271
|
-
format ||
|
|
272
|
-
(bundle.type === "esm" ? "modules" : "service-worker") === "modules"
|
|
273
|
-
? "true"
|
|
274
|
-
: "false",
|
|
275
|
-
]);
|
|
276
|
-
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
277
|
-
|
|
278
|
-
local.current.on("close", (code) => {
|
|
279
|
-
if (code !== null) {
|
|
280
|
-
console.log(`miniflare process exited with code ${code}`);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
local.current.stdout.on("data", (_data: string) => {
|
|
285
|
-
// console.log(`stdout: ${data}`);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
local.current.stderr.on("data", (data: string) => {
|
|
289
|
-
// console.error(`stderr: ${data}`);
|
|
290
|
-
const matches =
|
|
291
|
-
/Debugger listening on (ws:\/\/127\.0\.0\.1:9229\/[A-Za-z0-9-]+)/.exec(
|
|
292
|
-
data
|
|
293
|
-
);
|
|
294
|
-
if (matches) {
|
|
295
|
-
setInspectorUrl(matches[1]);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
removeSignalExitListener.current = onExit((_code, _signal) => {
|
|
300
|
-
console.log("⎔ Shutting down local server.");
|
|
301
|
-
local.current?.kill();
|
|
302
|
-
local.current = undefined;
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
startLocalWorker();
|
|
307
|
-
|
|
308
|
-
return () => {
|
|
309
|
-
if (local.current) {
|
|
310
|
-
console.log("⎔ Shutting down local server.");
|
|
311
|
-
local.current?.kill();
|
|
312
|
-
local.current = undefined;
|
|
313
|
-
removeSignalExitListener.current && removeSignalExitListener.current();
|
|
314
|
-
removeSignalExitListener.current = undefined;
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
}, [bundle, format, port]);
|
|
318
|
-
return { inspectorUrl };
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function useTmpDir(): string | void {
|
|
322
|
-
const [directory, setDirectory] = useState<DirectoryResult>();
|
|
323
|
-
useEffect(() => {
|
|
324
|
-
let dir: DirectoryResult;
|
|
325
|
-
async function create() {
|
|
326
|
-
try {
|
|
327
|
-
dir = await tmp.dir({ unsafeCleanup: true });
|
|
328
|
-
setDirectory(dir);
|
|
329
|
-
return;
|
|
330
|
-
} catch (err) {
|
|
331
|
-
console.error("failed to create tmp dir");
|
|
332
|
-
console.error(err);
|
|
333
|
-
throw err;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
create();
|
|
337
|
-
return () => {
|
|
338
|
-
dir.cleanup();
|
|
339
|
-
};
|
|
340
|
-
}, []);
|
|
341
|
-
return directory?.path;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
type EsbuildBundle = {
|
|
345
|
-
id: number;
|
|
346
|
-
path: string;
|
|
347
|
-
entry: string;
|
|
348
|
-
type: "esm" | "commonjs";
|
|
349
|
-
exports: string[];
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
function useEsbuild(props: {
|
|
353
|
-
entry: string;
|
|
354
|
-
destination: string | void;
|
|
355
|
-
staticRoot: void | string;
|
|
356
|
-
jsxFactory: string | void;
|
|
357
|
-
jsxFragment: string | void;
|
|
358
|
-
polyfillNode: boolean | void;
|
|
359
|
-
}): EsbuildBundle | void {
|
|
360
|
-
const {
|
|
361
|
-
entry,
|
|
362
|
-
destination,
|
|
363
|
-
staticRoot,
|
|
364
|
-
jsxFactory,
|
|
365
|
-
jsxFragment,
|
|
366
|
-
polyfillNode,
|
|
367
|
-
} = props;
|
|
368
|
-
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
369
|
-
useEffect(() => {
|
|
370
|
-
let result: esbuild.BuildResult;
|
|
371
|
-
async function build() {
|
|
372
|
-
if (!destination) return;
|
|
373
|
-
result = await esbuild.build({
|
|
374
|
-
entryPoints: [entry],
|
|
375
|
-
define: {
|
|
376
|
-
...(polyfillNode && { global: "globalThis" }),
|
|
377
|
-
},
|
|
378
|
-
bundle: true,
|
|
379
|
-
outdir: destination,
|
|
380
|
-
metafile: true,
|
|
381
|
-
format: "esm",
|
|
382
|
-
sourcemap: true,
|
|
383
|
-
loader: {
|
|
384
|
-
".js": "jsx",
|
|
385
|
-
},
|
|
386
|
-
plugins: polyfillNode
|
|
387
|
-
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
388
|
-
: undefined,
|
|
389
|
-
...(jsxFactory && { jsxFactory }),
|
|
390
|
-
...(jsxFragment && { jsxFragment }),
|
|
391
|
-
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
392
|
-
// TODO: import.meta.url
|
|
393
|
-
watch: {
|
|
394
|
-
async onRebuild(error) {
|
|
395
|
-
if (error) console.error("watch build failed:", error);
|
|
396
|
-
else {
|
|
397
|
-
// nothing really changes here, so let's increment the id
|
|
398
|
-
// to change the return object's identity
|
|
399
|
-
setBundle((bundle) => ({ ...bundle, id: bundle.id + 1 }));
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
const chunks = Object.entries(result.metafile.outputs).find(
|
|
406
|
-
([_path, { entryPoint }]) => entryPoint === entry
|
|
407
|
-
); // assumedly only one entry point
|
|
408
|
-
|
|
409
|
-
setBundle({
|
|
410
|
-
id: 0,
|
|
411
|
-
entry,
|
|
412
|
-
path: chunks[0],
|
|
413
|
-
type: chunks[1].exports.length > 0 ? "esm" : "commonjs",
|
|
414
|
-
exports: chunks[1].exports,
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
build();
|
|
418
|
-
return () => {
|
|
419
|
-
result?.stop();
|
|
420
|
-
};
|
|
421
|
-
}, [entry, destination, staticRoot, jsxFactory, jsxFragment, polyfillNode]);
|
|
422
|
-
return bundle;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function useWorker(props: {
|
|
426
|
-
name: void | string;
|
|
427
|
-
bundle: EsbuildBundle | void;
|
|
428
|
-
format: CfScriptFormat;
|
|
429
|
-
modules: CfModule[];
|
|
430
|
-
accountId: string;
|
|
431
|
-
apiToken: string;
|
|
432
|
-
variables: { [name: string]: CfVariable };
|
|
433
|
-
sitesFolder: void | string;
|
|
434
|
-
port: number;
|
|
435
|
-
compatibilityDate: string | void;
|
|
436
|
-
compatibilityFlags: string[] | void;
|
|
437
|
-
usageModel: void | "bundled" | "unbound";
|
|
438
|
-
}): CfPreviewToken | void {
|
|
439
|
-
const {
|
|
440
|
-
name,
|
|
441
|
-
bundle,
|
|
442
|
-
format,
|
|
443
|
-
modules,
|
|
444
|
-
accountId,
|
|
445
|
-
apiToken,
|
|
446
|
-
variables,
|
|
447
|
-
sitesFolder,
|
|
448
|
-
compatibilityDate,
|
|
449
|
-
compatibilityFlags,
|
|
450
|
-
usageModel,
|
|
451
|
-
port,
|
|
452
|
-
} = props;
|
|
453
|
-
const [token, setToken] = useState<CfPreviewToken>();
|
|
454
|
-
useEffect(() => {
|
|
455
|
-
async function start() {
|
|
456
|
-
if (!bundle) return;
|
|
457
|
-
if (format === "modules" && bundle.type === "commonjs") {
|
|
458
|
-
console.error("⎔ Cannot use modules with a commonjs bundle.");
|
|
459
|
-
// TODO: a much better error message here, with what to do next
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
if (format === "service-worker" && bundle.type !== "esm") {
|
|
463
|
-
console.error("⎔ Cannot use service-worker with a esm bundle.");
|
|
464
|
-
// TODO: a much better error message here, with what to do next
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (token) {
|
|
469
|
-
console.log("⎔ Detected changes, restarting server...");
|
|
470
|
-
} else {
|
|
471
|
-
console.log("⎔ Starting server...");
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const assets = sitesFolder
|
|
475
|
-
? await syncAssets(
|
|
476
|
-
accountId,
|
|
477
|
-
path.basename(bundle.path),
|
|
478
|
-
sitesFolder,
|
|
479
|
-
true,
|
|
480
|
-
undefined // TODO: env
|
|
481
|
-
)
|
|
482
|
-
: {
|
|
483
|
-
manifest: undefined,
|
|
484
|
-
namespace: undefined,
|
|
485
|
-
}; // TODO: cancellable?
|
|
486
|
-
|
|
487
|
-
const content = await readFile(bundle.path, "utf-8");
|
|
488
|
-
const init: CfWorkerInit = {
|
|
489
|
-
name: name || path.basename(bundle.path),
|
|
490
|
-
main: {
|
|
491
|
-
name: path.basename(bundle.path),
|
|
492
|
-
type: format || bundle.type === "esm" ? "esm" : "commonjs",
|
|
493
|
-
content,
|
|
494
|
-
},
|
|
495
|
-
modules: assets.manifest
|
|
496
|
-
? modules.concat({
|
|
497
|
-
name: "__STATIC_CONTENT_MANIFEST",
|
|
498
|
-
content: JSON.stringify(assets.manifest),
|
|
499
|
-
type: "text",
|
|
500
|
-
})
|
|
501
|
-
: modules,
|
|
502
|
-
variables: assets.namespace
|
|
503
|
-
? {
|
|
504
|
-
...variables,
|
|
505
|
-
__STATIC_CONTENT: { namespaceId: assets.namespace },
|
|
506
|
-
}
|
|
507
|
-
: variables,
|
|
508
|
-
migrations: undefined, // no migrations in dev
|
|
509
|
-
compatibility_date: compatibilityDate,
|
|
510
|
-
compatibility_flags: compatibilityFlags,
|
|
511
|
-
usage_model: usageModel,
|
|
512
|
-
};
|
|
513
|
-
setToken(
|
|
514
|
-
await createWorker(init, {
|
|
515
|
-
accountId,
|
|
516
|
-
apiToken,
|
|
517
|
-
})
|
|
518
|
-
);
|
|
519
|
-
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
520
|
-
}
|
|
521
|
-
start();
|
|
522
|
-
}, [
|
|
523
|
-
name,
|
|
524
|
-
bundle,
|
|
525
|
-
format,
|
|
526
|
-
accountId,
|
|
527
|
-
apiToken,
|
|
528
|
-
port,
|
|
529
|
-
sitesFolder,
|
|
530
|
-
compatibilityDate,
|
|
531
|
-
compatibilityFlags,
|
|
532
|
-
usageModel,
|
|
533
|
-
]);
|
|
534
|
-
return token;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function useProxy({
|
|
538
|
-
token,
|
|
539
|
-
publicRoot,
|
|
540
|
-
port,
|
|
541
|
-
}: {
|
|
542
|
-
token: CfPreviewToken | void;
|
|
543
|
-
publicRoot: void | string;
|
|
544
|
-
port: number;
|
|
545
|
-
}) {
|
|
546
|
-
useEffect(() => {
|
|
547
|
-
if (!token) return;
|
|
548
|
-
const proxy = httpProxy.createProxyServer({
|
|
549
|
-
secure: false,
|
|
550
|
-
changeOrigin: true,
|
|
551
|
-
headers: {
|
|
552
|
-
"cf-workers-preview-token": token.value,
|
|
553
|
-
},
|
|
554
|
-
target: `https://${token.host}`,
|
|
555
|
-
// TODO: log websockets too? validate durables, etc
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const servePublic =
|
|
559
|
-
publicRoot &&
|
|
560
|
-
serveStatic(publicRoot, {
|
|
561
|
-
cacheControl: false,
|
|
562
|
-
});
|
|
563
|
-
const server = http
|
|
564
|
-
.createServer((req, res) => {
|
|
565
|
-
if (publicRoot) {
|
|
566
|
-
servePublic(req, res, () => {
|
|
567
|
-
proxy.web(req, res);
|
|
568
|
-
});
|
|
569
|
-
} else {
|
|
570
|
-
proxy.web(req, res);
|
|
571
|
-
}
|
|
572
|
-
})
|
|
573
|
-
.listen(port); // TODO: custom port
|
|
574
|
-
|
|
575
|
-
proxy.on("proxyRes", function (proxyRes, req, res) {
|
|
576
|
-
// log all requests
|
|
577
|
-
console.log(
|
|
578
|
-
new Date().toLocaleTimeString(),
|
|
579
|
-
req.method,
|
|
580
|
-
req.url,
|
|
581
|
-
res.statusCode // TODO add a status message like Ok etc?
|
|
582
|
-
);
|
|
583
|
-
});
|
|
584
|
-
// TODO: log errors?
|
|
585
|
-
|
|
586
|
-
return () => {
|
|
587
|
-
proxy.close();
|
|
588
|
-
server.close();
|
|
589
|
-
};
|
|
590
|
-
}, [token, publicRoot, port]);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function useInspector(inspectorUrl: string | void) {
|
|
594
|
-
useEffect(() => {
|
|
595
|
-
if (!inspectorUrl) return;
|
|
596
|
-
|
|
597
|
-
const inspector = new DtInspector(inspectorUrl);
|
|
598
|
-
const abortController = inspector.proxyTo(9229);
|
|
599
|
-
return () => {
|
|
600
|
-
inspector.close();
|
|
601
|
-
abortController.abort();
|
|
602
|
-
};
|
|
603
|
-
}, [inspectorUrl]);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
function sleep(period: number) {
|
|
607
|
-
return new Promise((resolve) => setTimeout(resolve, period));
|
|
608
|
-
}
|
|
609
|
-
const SLEEP_DURATION = 2000;
|
|
610
|
-
// really need a first class api for this
|
|
611
|
-
const hostNameRegex = /userHostname="(.*)"/g;
|
|
612
|
-
async function findTunnelHostname() {
|
|
613
|
-
let hostName: string;
|
|
614
|
-
while (!hostName) {
|
|
615
|
-
try {
|
|
616
|
-
const resp = await fetch("http://localhost:8789/metrics");
|
|
617
|
-
const data = await resp.text();
|
|
618
|
-
const matches = Array.from(data.matchAll(hostNameRegex));
|
|
619
|
-
hostName = matches[0][1];
|
|
620
|
-
} catch (err) {
|
|
621
|
-
await sleep(SLEEP_DURATION);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
return hostName;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
function useTunnel(toggle: boolean) {
|
|
628
|
-
const tunnel = useRef<ReturnType<typeof spawn>>();
|
|
629
|
-
const removeSignalExitListener = useRef<() => void>();
|
|
630
|
-
// TODO: test if cloudflared is available, if not
|
|
631
|
-
// point them to a url where they can get docs to install it
|
|
632
|
-
useEffect(() => {
|
|
633
|
-
async function startTunnel() {
|
|
634
|
-
if (toggle) {
|
|
635
|
-
try {
|
|
636
|
-
await commandExists("cloudflared");
|
|
637
|
-
} catch (e) {
|
|
638
|
-
console.error(
|
|
639
|
-
"To share your worker on the internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
|
|
640
|
-
);
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
console.log("⎔ Starting a tunnel...");
|
|
644
|
-
tunnel.current = spawn("cloudflared", [
|
|
645
|
-
"tunnel",
|
|
646
|
-
"--url",
|
|
647
|
-
"http://localhost:8787",
|
|
648
|
-
"--metrics",
|
|
649
|
-
"localhost:8789",
|
|
650
|
-
]);
|
|
651
|
-
|
|
652
|
-
tunnel.current.on("close", (code) => {
|
|
653
|
-
if (code !== 0) {
|
|
654
|
-
console.log(`Tunnel process exited with code ${code}`);
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
removeSignalExitListener.current = onExit((_code, _signal) => {
|
|
659
|
-
console.log("⎔ Shutting down local tunnel.");
|
|
660
|
-
tunnel.current?.kill();
|
|
661
|
-
tunnel.current = undefined;
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
const hostName = await findTunnelHostname();
|
|
665
|
-
clipboardy.write(hostName);
|
|
666
|
-
console.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
startTunnel();
|
|
671
|
-
|
|
672
|
-
return () => {
|
|
673
|
-
if (tunnel.current) {
|
|
674
|
-
console.log("⎔ Shutting down tunnel.");
|
|
675
|
-
tunnel.current?.kill();
|
|
676
|
-
tunnel.current = undefined;
|
|
677
|
-
removeSignalExitListener.current && removeSignalExitListener.current();
|
|
678
|
-
removeSignalExitListener.current = undefined;
|
|
679
|
-
}
|
|
680
|
-
};
|
|
681
|
-
}, [toggle]);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
type useHotkeysInitialState = {
|
|
685
|
-
local: boolean;
|
|
686
|
-
tunnel: boolean;
|
|
687
|
-
};
|
|
688
|
-
function useHotkeys(initial: useHotkeysInitialState, port: number) {
|
|
689
|
-
// UGH, we should put port in context instead
|
|
690
|
-
const [toggles, setToggles] = useState(initial);
|
|
691
|
-
useInput(
|
|
692
|
-
(
|
|
693
|
-
input,
|
|
694
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
695
|
-
key
|
|
696
|
-
) => {
|
|
697
|
-
switch (input) {
|
|
698
|
-
case "b": // open browser
|
|
699
|
-
open(
|
|
700
|
-
`http://localhost:${port}/`
|
|
701
|
-
// {
|
|
702
|
-
// app: {
|
|
703
|
-
// name: open.apps.chrome, // TODO: fallback on other browsers
|
|
704
|
-
// },
|
|
705
|
-
// }
|
|
706
|
-
);
|
|
707
|
-
break;
|
|
708
|
-
case "d": // toggle inspector
|
|
709
|
-
open(
|
|
710
|
-
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:9229/ws`
|
|
711
|
-
// {
|
|
712
|
-
// app: {
|
|
713
|
-
// name: open.apps.chrome,
|
|
714
|
-
// // todo - add firefox and edge fallbacks
|
|
715
|
-
// },
|
|
716
|
-
// }
|
|
717
|
-
);
|
|
718
|
-
break;
|
|
719
|
-
case "s": // toggle tunnel
|
|
720
|
-
setToggles((toggles) => ({ ...toggles, tunnel: !toggles.tunnel }));
|
|
721
|
-
break;
|
|
722
|
-
case "l": // toggle local
|
|
723
|
-
setToggles((toggles) => ({ ...toggles, local: !toggles.local }));
|
|
724
|
-
break;
|
|
725
|
-
case "q": // shut down
|
|
726
|
-
case "x": // shut down
|
|
727
|
-
process.exit(0);
|
|
728
|
-
break;
|
|
729
|
-
default:
|
|
730
|
-
// nothing?
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
);
|
|
735
|
-
return toggles;
|
|
736
|
-
}
|