wrangler 0.0.0-cd05d20 → 0.0.0-ce2ef20
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/package.json +7 -6
- package/src/__tests__/index.test.ts +159 -63
- package/src/__tests__/mock-cfetch.js +6 -13
- package/src/api/inspect.ts +0 -11
- package/src/dev.tsx +66 -13
- package/src/dialogs.tsx +1 -9
- package/src/index.tsx +54 -21
- package/src/module-collection.ts +4 -3
- package/src/pages.tsx +586 -80
- package/src/publish.ts +4 -3
- package/src/user.tsx +1 -1
- package/wrangler-dist/cli.js +53546 -66058
- package/wrangler-dist/cli.js.map +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrangler",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-ce2ef20",
|
|
4
4
|
"author": "wrangler@cloudflare.com",
|
|
5
5
|
"description": "Command-line interface for all things Cloudflare Workers",
|
|
6
6
|
"bin": {
|
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
"@cloudflare/pages-functions-compiler": "0.3.8",
|
|
40
40
|
"esbuild": "0.14.1",
|
|
41
41
|
"miniflare": "2.0.0-rc.3",
|
|
42
|
-
"semiver": "^1.1.0"
|
|
43
|
-
"serve": "^13.0.2"
|
|
42
|
+
"semiver": "^1.1.0"
|
|
44
43
|
},
|
|
45
44
|
"optionalDependencies": {
|
|
46
45
|
"fsevents": "~2.3.2"
|
|
@@ -53,15 +52,14 @@
|
|
|
53
52
|
"@types/signal-exit": "^3.0.1",
|
|
54
53
|
"@types/ws": "^8.2.1",
|
|
55
54
|
"@types/yargs": "^17.0.7",
|
|
55
|
+
"chokidar": "^3.5.2",
|
|
56
56
|
"clipboardy": "^3.0.0",
|
|
57
57
|
"command-exists": "^1.2.9",
|
|
58
58
|
"execa": "^6.0.0",
|
|
59
|
-
"express": "^4.17.1",
|
|
60
59
|
"finalhandler": "^1.1.2",
|
|
61
60
|
"find-up": "^6.2.0",
|
|
62
61
|
"formdata-node": "^4.3.1",
|
|
63
62
|
"http-proxy": "^1.18.1",
|
|
64
|
-
"http-proxy-middleware": "^2.0.1",
|
|
65
63
|
"ink": "^3.2.0",
|
|
66
64
|
"ink-select-input": "^4.2.1",
|
|
67
65
|
"ink-table": "^3.0.0",
|
|
@@ -73,8 +71,10 @@
|
|
|
73
71
|
"serve-static": "^1.14.1",
|
|
74
72
|
"signal-exit": "^3.0.6",
|
|
75
73
|
"tmp-promise": "^3.0.3",
|
|
74
|
+
"undici": "^4.11.1",
|
|
76
75
|
"ws": "^8.3.0",
|
|
77
|
-
"yargs": "^17.3.0"
|
|
76
|
+
"yargs": "^17.3.0",
|
|
77
|
+
"react-error-boundary": "^3.1.4"
|
|
78
78
|
},
|
|
79
79
|
"files": [
|
|
80
80
|
"src",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"node": ">=16.7.0"
|
|
97
97
|
},
|
|
98
98
|
"jest": {
|
|
99
|
+
"restoreMocks": true,
|
|
99
100
|
"testRegex": ".*.(test|spec)\\.[jt]sx?$",
|
|
100
101
|
"transformIgnorePatterns": [
|
|
101
102
|
"node_modules/(?!node-fetch|fetch-blob|find-up|locate-path|p-locate|p-limit|yocto-queue|path-exists|data-uri-to-buffer|formdata-polyfill|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream)"
|
|
@@ -3,72 +3,119 @@ import * as fsp from "node:fs/promises";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as TOML from "@iarna/toml";
|
|
5
5
|
import { main } from "../index";
|
|
6
|
-
|
|
7
|
-
import {
|
|
6
|
+
import { setMock, unsetAllMocks } from "./mock-cfetch";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { confirm } from "../dialogs";
|
|
8
9
|
|
|
9
|
-
jest.mock("../cfetch", () =>
|
|
10
|
-
return jest.requireActual("./mock-cfetch");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
async function w(cmd: void | string, options?: { tap: boolean }) {
|
|
14
|
-
const tapped = options?.tap ? tap() : undefined;
|
|
15
|
-
await main([...(cmd ? cmd.split(" ") : [])]);
|
|
16
|
-
tapped?.off();
|
|
17
|
-
return { stdout: tapped?.out, stderr: tapped?.err };
|
|
18
|
-
}
|
|
10
|
+
jest.mock("../cfetch", () => jest.requireActual("./mock-cfetch"));
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const toReturn = {
|
|
25
|
-
off: () => {
|
|
26
|
-
console.log = oldLog;
|
|
27
|
-
console.error = oldError;
|
|
28
|
-
},
|
|
29
|
-
out: "",
|
|
30
|
-
err: "",
|
|
12
|
+
jest.mock("../dialogs", () => {
|
|
13
|
+
return {
|
|
14
|
+
...jest.requireActual<object>("../dialogs"),
|
|
15
|
+
confirm: jest.fn().mockName("confirmMock"),
|
|
31
16
|
};
|
|
17
|
+
});
|
|
32
18
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Mock the implementation of `confirm()` that will respond with configured results
|
|
21
|
+
* for configured confirmation text messages.
|
|
22
|
+
*
|
|
23
|
+
* If there is a call to `confirm()` that does not match any of the expectations
|
|
24
|
+
* then an error is thrown.
|
|
25
|
+
*/
|
|
26
|
+
function mockConfirm(...expectations: { text: string; result: boolean }[]) {
|
|
27
|
+
(confirm as jest.Mock).mockImplementationOnce((text: string) => {
|
|
28
|
+
for (const { text: expectedText, result } of expectations) {
|
|
29
|
+
if (text === expectedText) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Unexpected confirmation message: ${text}`);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
async function w(cmd?: string) {
|
|
38
|
+
const logSpy = jest.spyOn(console, "log").mockImplementation();
|
|
39
|
+
const errorSpy = jest.spyOn(console, "error").mockImplementation();
|
|
40
|
+
const warnSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
41
|
+
try {
|
|
42
|
+
await main(cmd?.split(" ") ?? []);
|
|
43
|
+
return {
|
|
44
|
+
stdout: logSpy.mock.calls.flat(2).join("\n"),
|
|
45
|
+
stderr: errorSpy.mock.calls.flat(2).join("\n"),
|
|
46
|
+
warnings: warnSpy.mock.calls.flat(2).join("\n"),
|
|
47
|
+
};
|
|
48
|
+
} finally {
|
|
49
|
+
logSpy.mockRestore();
|
|
50
|
+
errorSpy.mockRestore();
|
|
51
|
+
warnSpy.mockRestore();
|
|
52
|
+
}
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
describe("wrangler", () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
describe("no command", () => {
|
|
57
|
+
it("should display a list of available commands", async () => {
|
|
58
|
+
const { stdout, stderr } = await w();
|
|
59
|
+
|
|
60
|
+
expect(stdout).toMatchInlineSnapshot(`
|
|
61
|
+
"wrangler
|
|
62
|
+
|
|
63
|
+
Commands:
|
|
64
|
+
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
65
|
+
wrangler dev <filename> 👂 Start a local server for developing your worker
|
|
66
|
+
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
67
|
+
wrangler tail [name] 🦚 Starts a log tailing session for a deployed Worker.
|
|
68
|
+
wrangler secret 🤫 Generate a secret that can be referenced in the worker script
|
|
69
|
+
wrangler kv:namespace 🗂️ Interact with your Workers KV Namespaces
|
|
70
|
+
wrangler kv:key 🔑 Individually manage Workers KV key-value pairs
|
|
71
|
+
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
|
|
72
|
+
wrangler pages ⚡️ Configure Cloudflare Pages
|
|
73
|
+
|
|
74
|
+
Flags:
|
|
75
|
+
-c, --config Path to .toml configuration file [string]
|
|
76
|
+
-h, --help Show help [boolean]
|
|
77
|
+
-v, --version Show version number [boolean]
|
|
78
|
+
|
|
79
|
+
Options:
|
|
80
|
+
-l, --local Run on my machine [boolean] [default: false]"
|
|
81
|
+
`);
|
|
82
|
+
|
|
83
|
+
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("invalid command", () => {
|
|
88
|
+
it("should display an error", async () => {
|
|
89
|
+
const { stdout, stderr } = await w("invalid-command");
|
|
90
|
+
|
|
91
|
+
expect(stdout).toMatchInlineSnapshot(`
|
|
92
|
+
"wrangler
|
|
93
|
+
|
|
94
|
+
Commands:
|
|
95
|
+
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
96
|
+
wrangler dev <filename> 👂 Start a local server for developing your worker
|
|
97
|
+
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
98
|
+
wrangler tail [name] 🦚 Starts a log tailing session for a deployed Worker.
|
|
99
|
+
wrangler secret 🤫 Generate a secret that can be referenced in the worker script
|
|
100
|
+
wrangler kv:namespace 🗂️ Interact with your Workers KV Namespaces
|
|
101
|
+
wrangler kv:key 🔑 Individually manage Workers KV key-value pairs
|
|
102
|
+
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
|
|
103
|
+
wrangler pages ⚡️ Configure Cloudflare Pages
|
|
104
|
+
|
|
105
|
+
Flags:
|
|
106
|
+
-c, --config Path to .toml configuration file [string]
|
|
107
|
+
-h, --help Show help [boolean]
|
|
108
|
+
-v, --version Show version number [boolean]
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
-l, --local Run on my machine [boolean] [default: false]"
|
|
112
|
+
`);
|
|
113
|
+
|
|
114
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
115
|
+
"
|
|
116
|
+
Unknown command: invalid-command."
|
|
117
|
+
`);
|
|
118
|
+
});
|
|
72
119
|
});
|
|
73
120
|
|
|
74
121
|
describe("init", () => {
|
|
@@ -79,7 +126,9 @@ describe("wrangler", () => {
|
|
|
79
126
|
});
|
|
80
127
|
|
|
81
128
|
afterEach(async () => {
|
|
82
|
-
|
|
129
|
+
if (existsSync("./wrangler.toml")) {
|
|
130
|
+
await fsp.rm("./wrangler.toml");
|
|
131
|
+
}
|
|
83
132
|
process.chdir(ogcwd);
|
|
84
133
|
});
|
|
85
134
|
|
|
@@ -89,10 +138,57 @@ describe("wrangler", () => {
|
|
|
89
138
|
expect(typeof parsed.compatibility_date).toBe("string");
|
|
90
139
|
});
|
|
91
140
|
|
|
92
|
-
it("should
|
|
141
|
+
it("should display warning when wrangler.toml already exists, and exit if user does not want to carry on", async () => {
|
|
93
142
|
fs.closeSync(fs.openSync("./wrangler.toml", "w"));
|
|
94
|
-
|
|
95
|
-
|
|
143
|
+
mockConfirm({
|
|
144
|
+
text: "Do you want to continue initializing this project?",
|
|
145
|
+
result: false,
|
|
146
|
+
});
|
|
147
|
+
const { stderr } = await w("init");
|
|
148
|
+
expect(stderr).toContain("wrangler.toml file already exists!");
|
|
149
|
+
const parsed = TOML.parse(await fsp.readFile("./wrangler.toml", "utf-8"));
|
|
150
|
+
expect(typeof parsed.compatibility_date).toBe("undefined");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should display warning when wrangler.toml already exists, but continue if user does want to carry on", async () => {
|
|
154
|
+
fs.closeSync(fs.openSync("./wrangler.toml", "w"));
|
|
155
|
+
mockConfirm({
|
|
156
|
+
text: "Do you want to continue initializing this project?",
|
|
157
|
+
result: true,
|
|
158
|
+
});
|
|
159
|
+
const { stderr } = await w("init");
|
|
160
|
+
expect(stderr).toContain("wrangler.toml file already exists!");
|
|
161
|
+
const parsed = TOML.parse(await fsp.readFile("./wrangler.toml", "utf-8"));
|
|
162
|
+
expect(typeof parsed.compatibility_date).toBe("string");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should error if `--type` is used", async () => {
|
|
166
|
+
const noValue = await w("init --type");
|
|
167
|
+
expect(noValue.stderr).toMatchInlineSnapshot(
|
|
168
|
+
`"The --type option is no longer supported."`
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should error if `--type javascript` is used", async () => {
|
|
173
|
+
const javascriptValue = await w("init --type javascript");
|
|
174
|
+
expect(javascriptValue.stderr).toMatchInlineSnapshot(
|
|
175
|
+
`"The --type option is no longer supported."`
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should error if `--type rust` is used", async () => {
|
|
180
|
+
const rustValue = await w("init --type rust");
|
|
181
|
+
expect(rustValue.stderr).toMatchInlineSnapshot(
|
|
182
|
+
`"The --type option is no longer supported."`
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should error if `--type webpack` is used", async () => {
|
|
187
|
+
const webpackValue = await w("init --type webpack");
|
|
188
|
+
expect(webpackValue.stderr).toMatchInlineSnapshot(`
|
|
189
|
+
"The --type option is no longer supported.
|
|
190
|
+
If you wish to use webpack then you will need to create a custom build."
|
|
191
|
+
`);
|
|
96
192
|
});
|
|
97
193
|
});
|
|
98
194
|
|
|
@@ -125,7 +221,7 @@ describe("wrangler", () => {
|
|
|
125
221
|
return KVNamespaces;
|
|
126
222
|
}
|
|
127
223
|
);
|
|
128
|
-
const { stdout } = await w("kv:namespace list"
|
|
224
|
+
const { stdout } = await w("kv:namespace list");
|
|
129
225
|
const namespaces = JSON.parse(stdout);
|
|
130
226
|
createdNamespace = namespaces.find(
|
|
131
227
|
(ns) => ns.title === "worker-UnitTestNamespace"
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// This file mocks ../cfetch.ts
|
|
2
2
|
// so we can insert whatever responses we want from it
|
|
3
3
|
|
|
4
|
-
const pathToRegexp = require("path-to-regexp");
|
|
4
|
+
const { pathToRegexp } = require("path-to-regexp");
|
|
5
5
|
// TODO: add jsdoc style types here
|
|
6
6
|
|
|
7
7
|
// type MockHandler = (resource: string, init?: RequestInit) => any; // TODO: use a generic here
|
|
8
8
|
|
|
9
9
|
let mocks = [];
|
|
10
10
|
|
|
11
|
-
function mockCfetch(resource, init) {
|
|
11
|
+
export function mockCfetch(resource, init) {
|
|
12
12
|
for (const { regexp, handler } of mocks) {
|
|
13
13
|
if (regexp.test(resource)) {
|
|
14
14
|
return handler(resource, init); // should we have some kind of fallthrough system? we'll see.
|
|
@@ -17,7 +17,7 @@ function mockCfetch(resource, init) {
|
|
|
17
17
|
throw new Error(`no mocks found for ${resource}`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function setMock(resource, handler) {
|
|
20
|
+
export function setMock(resource, handler) {
|
|
21
21
|
const mock = {
|
|
22
22
|
resource,
|
|
23
23
|
handler,
|
|
@@ -29,18 +29,11 @@ function setMock(resource, handler) {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function unsetAllMocks() {
|
|
32
|
+
export function unsetAllMocks() {
|
|
33
33
|
mocks = [];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const CF_API_BASE_URL =
|
|
36
|
+
export const CF_API_BASE_URL =
|
|
37
37
|
process.env.CF_API_BASE_URL || "https://api.cloudflare.com/client/v4";
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
__esModule: true,
|
|
41
|
-
default: mockCfetch,
|
|
42
|
-
mockCfetch,
|
|
43
|
-
setMock,
|
|
44
|
-
unsetAllMocks,
|
|
45
|
-
CF_API_BASE_URL,
|
|
46
|
-
});
|
|
39
|
+
export default mockCfetch;
|
package/src/api/inspect.ts
CHANGED
|
@@ -140,17 +140,6 @@ export type DtEvent = DtLogEvent | DtExceptionEvent;
|
|
|
140
140
|
*/
|
|
141
141
|
export type DtListener = (event: DtEvent) => void;
|
|
142
142
|
|
|
143
|
-
interface DtProtocolRequest<T> {
|
|
144
|
-
id: number;
|
|
145
|
-
method: string;
|
|
146
|
-
params?: T;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
interface DtProtocolResponse<T> {
|
|
150
|
-
id: number;
|
|
151
|
-
result: T;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
143
|
/**
|
|
155
144
|
* A DevTools inspector that listens to logs and debug events from a Worker.
|
|
156
145
|
*
|
package/src/dev.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import assert from "assert";
|
|
|
23
23
|
import { getAPIToken } from "./user";
|
|
24
24
|
import fetch from "node-fetch";
|
|
25
25
|
import makeModuleCollector from "./module-collection";
|
|
26
|
+
import { withErrorBoundary, useErrorHandler } from "react-error-boundary";
|
|
26
27
|
|
|
27
28
|
type CfScriptFormat = void | "modules" | "service-worker";
|
|
28
29
|
|
|
@@ -43,7 +44,7 @@ type Props = {
|
|
|
43
44
|
usageModel: void | "bundled" | "unbound";
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
function Dev(props: Props): JSX.Element {
|
|
47
48
|
if (props.public && props.format === "service-worker") {
|
|
48
49
|
throw new Error(
|
|
49
50
|
"You cannot use the service worker format with a `public` directory."
|
|
@@ -233,6 +234,7 @@ function useLocalWorker(props: {
|
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
console.log("⎔ Starting a local server...");
|
|
237
|
+
// TODO: just use execa for this
|
|
236
238
|
local.current = spawn("node", [
|
|
237
239
|
"--experimental-vm-modules",
|
|
238
240
|
"--inspect",
|
|
@@ -293,6 +295,17 @@ function useLocalWorker(props: {
|
|
|
293
295
|
}
|
|
294
296
|
});
|
|
295
297
|
|
|
298
|
+
local.current.on("exit", (code) => {
|
|
299
|
+
if (code !== 0) {
|
|
300
|
+
console.error(`miniflare process exited with code ${code}`);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
local.current.on("error", (error: Error) => {
|
|
305
|
+
console.error(`miniflare process failed to spawn`);
|
|
306
|
+
console.error(error);
|
|
307
|
+
});
|
|
308
|
+
|
|
296
309
|
removeSignalExitListener.current = onExit((_code, _signal) => {
|
|
297
310
|
console.log("⎔ Shutting down local server.");
|
|
298
311
|
local.current?.kill();
|
|
@@ -300,7 +313,9 @@ function useLocalWorker(props: {
|
|
|
300
313
|
});
|
|
301
314
|
}
|
|
302
315
|
|
|
303
|
-
startLocalWorker()
|
|
316
|
+
startLocalWorker().catch((err) => {
|
|
317
|
+
console.error("local worker:", err);
|
|
318
|
+
});
|
|
304
319
|
|
|
305
320
|
return () => {
|
|
306
321
|
if (local.current) {
|
|
@@ -317,6 +332,7 @@ function useLocalWorker(props: {
|
|
|
317
332
|
|
|
318
333
|
function useTmpDir(): string | void {
|
|
319
334
|
const [directory, setDirectory] = useState<DirectoryResult>();
|
|
335
|
+
const handleError = useErrorHandler();
|
|
320
336
|
useEffect(() => {
|
|
321
337
|
let dir: DirectoryResult;
|
|
322
338
|
async function create() {
|
|
@@ -326,15 +342,22 @@ function useTmpDir(): string | void {
|
|
|
326
342
|
return;
|
|
327
343
|
} catch (err) {
|
|
328
344
|
console.error("failed to create tmp dir");
|
|
329
|
-
console.error(err);
|
|
330
345
|
throw err;
|
|
331
346
|
}
|
|
332
347
|
}
|
|
333
|
-
create()
|
|
348
|
+
create().catch((err) => {
|
|
349
|
+
// we want to break here
|
|
350
|
+
// we can't do much without a temp dir anyway
|
|
351
|
+
handleError(err);
|
|
352
|
+
});
|
|
334
353
|
return () => {
|
|
335
|
-
dir.cleanup()
|
|
354
|
+
dir.cleanup().catch(() => {
|
|
355
|
+
// extremely unlikely,
|
|
356
|
+
// but it's 2021 after all
|
|
357
|
+
console.error("failed to cleanup tmp dir");
|
|
358
|
+
});
|
|
336
359
|
};
|
|
337
|
-
}, []);
|
|
360
|
+
}, [handleError]);
|
|
338
361
|
return directory?.path;
|
|
339
362
|
}
|
|
340
363
|
|
|
@@ -374,6 +397,7 @@ function useEsbuild(props: {
|
|
|
374
397
|
...(jsxFactory && { jsxFactory }),
|
|
375
398
|
...(jsxFragment && { jsxFragment }),
|
|
376
399
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
400
|
+
conditions: ["worker", "browser"],
|
|
377
401
|
plugins: [moduleCollector.plugin],
|
|
378
402
|
// TODO: import.meta.url
|
|
379
403
|
watch: {
|
|
@@ -401,7 +425,12 @@ function useEsbuild(props: {
|
|
|
401
425
|
modules: moduleCollector.modules,
|
|
402
426
|
});
|
|
403
427
|
}
|
|
404
|
-
build()
|
|
428
|
+
build().catch((_err) => {
|
|
429
|
+
// esbuild already logs errors to stderr
|
|
430
|
+
// and we don't want to end the process
|
|
431
|
+
// on build errors anyway
|
|
432
|
+
// so this is a no-op error handler
|
|
433
|
+
});
|
|
405
434
|
return () => {
|
|
406
435
|
result?.stop();
|
|
407
436
|
};
|
|
@@ -505,7 +534,11 @@ function useWorker(props: {
|
|
|
505
534
|
);
|
|
506
535
|
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
507
536
|
}
|
|
508
|
-
start()
|
|
537
|
+
start().catch((err) => {
|
|
538
|
+
// we want to log the error, but not end the process
|
|
539
|
+
// since it could recover after the developer fixes whatever's wrong
|
|
540
|
+
console.error("remote worker:", err);
|
|
541
|
+
});
|
|
509
542
|
}, [
|
|
510
543
|
name,
|
|
511
544
|
bundle,
|
|
@@ -649,12 +682,14 @@ function useTunnel(toggle: boolean) {
|
|
|
649
682
|
});
|
|
650
683
|
|
|
651
684
|
const hostName = await findTunnelHostname();
|
|
652
|
-
clipboardy.write(hostName);
|
|
685
|
+
await clipboardy.write(hostName);
|
|
653
686
|
console.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
|
|
654
687
|
}
|
|
655
688
|
}
|
|
656
689
|
|
|
657
|
-
startTunnel()
|
|
690
|
+
startTunnel().catch((err) => {
|
|
691
|
+
console.error("tunnel:", err);
|
|
692
|
+
});
|
|
658
693
|
|
|
659
694
|
return () => {
|
|
660
695
|
if (tunnel.current) {
|
|
@@ -676,14 +711,14 @@ function useHotkeys(initial: useHotkeysInitialState, port: number) {
|
|
|
676
711
|
// UGH, we should put port in context instead
|
|
677
712
|
const [toggles, setToggles] = useState(initial);
|
|
678
713
|
useInput(
|
|
679
|
-
(
|
|
714
|
+
async (
|
|
680
715
|
input,
|
|
681
716
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
682
717
|
key
|
|
683
718
|
) => {
|
|
684
719
|
switch (input) {
|
|
685
720
|
case "b": // open browser
|
|
686
|
-
open(
|
|
721
|
+
await open(
|
|
687
722
|
`http://localhost:${port}/`
|
|
688
723
|
// {
|
|
689
724
|
// app: {
|
|
@@ -693,7 +728,7 @@ function useHotkeys(initial: useHotkeysInitialState, port: number) {
|
|
|
693
728
|
);
|
|
694
729
|
break;
|
|
695
730
|
case "d": // toggle inspector
|
|
696
|
-
open(
|
|
731
|
+
await open(
|
|
697
732
|
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:9229/ws`
|
|
698
733
|
// {
|
|
699
734
|
// app: {
|
|
@@ -721,3 +756,21 @@ function useHotkeys(initial: useHotkeysInitialState, port: number) {
|
|
|
721
756
|
);
|
|
722
757
|
return toggles;
|
|
723
758
|
}
|
|
759
|
+
|
|
760
|
+
function ErrorFallback(props: {
|
|
761
|
+
error: Error;
|
|
762
|
+
resetErrorBoundary: () => void;
|
|
763
|
+
}) {
|
|
764
|
+
useEffect(() => {
|
|
765
|
+
console.error(props.error);
|
|
766
|
+
process.exit(1);
|
|
767
|
+
});
|
|
768
|
+
return (
|
|
769
|
+
<Box>
|
|
770
|
+
<Text>Something went wrong:</Text>
|
|
771
|
+
<Text>{props.error.message}</Text>
|
|
772
|
+
</Box>
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export default withErrorBoundary(Dev, { FallbackComponent: ErrorFallback });
|
package/src/dialogs.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { Box, Text, useInput, render } from "ink";
|
|
|
4
4
|
import TextInput from "ink-text-input";
|
|
5
5
|
type ConfirmProps = {
|
|
6
6
|
text: string;
|
|
7
|
-
onConfirm: (answer: boolean) => void
|
|
7
|
+
onConfirm: (answer: boolean) => void;
|
|
8
8
|
};
|
|
9
9
|
function Confirm(props: ConfirmProps) {
|
|
10
10
|
useInput((input: string) => {
|
|
@@ -75,11 +75,3 @@ export async function prompt(text: string, type: "text" | "password" = "text") {
|
|
|
75
75
|
);
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
// this one feels a little non "standard"
|
|
80
|
-
// export async function select(
|
|
81
|
-
// title: string,
|
|
82
|
-
// choices: { label: string; value: string }[]
|
|
83
|
-
// ): Promise<{ label: string; value: string }>{
|
|
84
|
-
|
|
85
|
-
// }
|