Skip to content

Commit 60123dd

Browse files
refactor: remove process.exit() from the pages code
This enables simpler testing, as we do not have to spawn new child processes to avoid the `process.exit()` from killing the jest process. As part of the refactor, some of the `Error` classes have been moved to a shared `errors.ts` file.
1 parent 6bc3e85 commit 60123dd

File tree

9 files changed

+85
-75
lines changed

9 files changed

+85
-75
lines changed

.changeset/polite-lemons-shop.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
refactor: remove `process.exit()` from the pages code
6+
7+
This enables simpler testing, as we do not have to spawn new child processes
8+
to avoid the `process.exit()` from killing the jest process.
9+
10+
As part of the refactor, some of the `Error` classes have been moved to a
11+
shared `errors.ts` file.

.vscode/settings.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"pgrep",
1818
"PKCE",
1919
"Positionals",
20+
"scandir",
2021
"selfsigned",
2122
"textfile",
2223
"tsbuildinfo",
@@ -28,7 +29,12 @@
2829
"websockets",
2930
"xxhash"
3031
],
31-
"cSpell.ignoreWords": ["TESTTEXTBLOBNAME", "TESTWASMNAME", "yxxx"],
32+
"cSpell.ignoreWords": [
33+
"TESTTEXTBLOBNAME",
34+
"TESTWASMNAME",
35+
"extensionless",
36+
"yxxx"
37+
],
3238
"eslint.runtime": "node",
3339
"files.trimTrailingWhitespace": true
3440
}

packages/wrangler/src/__tests__/index.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -845,14 +845,14 @@ describe("wrangler", () => {
845845
it("should throw an error if the deprecated command is used with positional arguments", async () => {
846846
await expect(runWrangler("preview GET")).rejects
847847
.toThrowErrorMatchingInlineSnapshot(`
848-
"DEPRECATION WARNING:
848+
"DEPRECATION:
849849
The \`wrangler preview\` command has been deprecated.
850850
Try using \`wrangler dev\` to to try out a worker during development.
851851
"
852852
`);
853853
await expect(runWrangler(`preview GET "SomeBody"`)).rejects
854854
.toThrowErrorMatchingInlineSnapshot(`
855-
"DEPRECATION WARNING:
855+
"DEPRECATION:
856856
The \`wrangler preview\` command has been deprecated.
857857
Try using \`wrangler dev\` to to try out a worker during development.
858858
"
@@ -970,15 +970,15 @@ describe("wrangler", () => {
970970
it("should print a deprecation message for 'generate'", async () => {
971971
await runWrangler("generate").catch((err) => {
972972
expect(err.message).toMatchInlineSnapshot(`
973-
"DEPRECATION WARNING:
973+
"DEPRECATION:
974974
\`wrangler generate\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#generate for alternatives"
975975
`);
976976
});
977977
});
978978
it("should print a deprecation message for 'build'", async () => {
979979
await runWrangler("build").catch((err) => {
980980
expect(err.message).toMatchInlineSnapshot(`
981-
"DEPRECATION WARNING:
981+
"DEPRECATION:
982982
\`wrangler build\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives"
983983
`);
984984
});
+21-45
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
1-
import { execaSync } from "execa";
2-
import { getPackageManager } from "../package-manager";
31
import { mockConsoleMethods } from "./helpers/mock-console";
42
import { runInTempDir } from "./helpers/run-in-tmp";
53
import { runWrangler } from "./helpers/run-wrangler";
6-
import type { PackageManager } from "../package-manager";
74

8-
describe("subcommand implicit help ran on imcomplete command execution", () => {
9-
let mockPackageManager: PackageManager;
5+
describe("subcommand implicit help ran on incomplete command execution", () => {
106
runInTempDir();
11-
12-
beforeEach(() => {
13-
mockPackageManager = {
14-
cwd: process.cwd(),
15-
// @ts-expect-error we're making a fake package manager here
16-
type: "mockpm",
17-
addDevDeps: jest.fn(),
18-
install: jest.fn(),
19-
};
20-
(getPackageManager as jest.Mock).mockResolvedValue(mockPackageManager);
21-
});
22-
237
const std = mockConsoleMethods();
248
function endEventLoop() {
259
return new Promise((resolve) => setImmediate(resolve));
@@ -46,36 +30,28 @@ describe("subcommand implicit help ran on imcomplete command execution", () => {
4630
🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"
4731
`);
4832
});
49-
});
5033

51-
describe("beta message for subcommands", () => {
52-
const betaMsg =
53-
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
54-
const isWindows = process.platform === "win32";
55-
it("should display for pages:dev", async () => {
56-
let err: Error | undefined;
57-
try {
58-
execaSync("npx", ["wrangler", "pages", "dev"], {
59-
shell: isWindows,
60-
env: { BROWSER: "none", ...process.env },
61-
}).stderr;
62-
} catch (e: unknown) {
63-
err = e as Error;
64-
}
65-
expect(err?.message.includes(betaMsg)).toBe(true);
66-
});
34+
describe("beta message for subcommands", () => {
35+
it("should display for pages:dev", async () => {
36+
await expect(
37+
runWrangler("pages dev")
38+
).rejects.toThrowErrorMatchingInlineSnapshot(
39+
`"Must specify a directory of static assets to serve or a command to run."`
40+
);
41+
42+
expect(std.out).toMatchInlineSnapshot(
43+
`"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"`
44+
);
45+
});
46+
47+
// Note that `wrangler pages functions` does nothing...
6748

68-
it("should display for pages:functions", async () => {
69-
let err: Error | undefined;
70-
try {
71-
execaSync("npx", ["wrangler", "pages", "functions", "build"], {
72-
shell: isWindows,
73-
env: { BROWSER: "none", ...process.env },
74-
});
75-
} catch (e: unknown) {
76-
err = e as Error;
77-
}
49+
it("should display for pages:functions:build", async () => {
50+
await expect(runWrangler("pages functions build")).rejects.toThrowError();
7851

79-
expect(err?.message.includes(betaMsg)).toBe(true);
52+
expect(std.out).toMatchInlineSnapshot(
53+
`"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"`
54+
);
55+
});
8056
});
8157
});

packages/wrangler/src/__tests__/route.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe("wrangler route", () => {
99
it("shows a deprecation notice when `wrangler route` is run", async () => {
1010
await expect(runWrangler("route")).rejects
1111
.toThrowErrorMatchingInlineSnapshot(`
12-
"DEPRECATION WARNING:
12+
"DEPRECATION:
1313
\`wrangler route\` has been deprecated.
1414
Please use wrangler.toml and/or \`wrangler publish --routes\` to modify routes"
1515
`);
@@ -18,7 +18,7 @@ describe("wrangler route", () => {
1818
it("shows a deprecation notice when `wrangler route delete` is run", async () => {
1919
await expect(runWrangler("route delete")).rejects
2020
.toThrowErrorMatchingInlineSnapshot(`
21-
"DEPRECATION WARNING:
21+
"DEPRECATION:
2222
\`wrangler route delete\` has been deprecated.
2323
Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes."
2424
`);
@@ -27,7 +27,7 @@ describe("wrangler route", () => {
2727
it("shows a deprecation notice when `wrangler route delete <id>` is run", async () => {
2828
await expect(runWrangler("route delete some-zone-id")).rejects
2929
.toThrowErrorMatchingInlineSnapshot(`
30-
"DEPRECATION WARNING:
30+
"DEPRECATION:
3131
\`wrangler route delete\` has been deprecated.
3232
Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes."
3333
`);
@@ -36,7 +36,7 @@ describe("wrangler route", () => {
3636
it("shows a deprecation notice when `wrangler route list` is run", async () => {
3737
await expect(runWrangler("route list")).rejects
3838
.toThrowErrorMatchingInlineSnapshot(`
39-
"DEPRECATION WARNING:
39+
"DEPRECATION:
4040
\`wrangler route list\` has been deprecated.
4141
Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.
4242
Refer to the Cloudflare Dashboard to see the routes this worker is currently running on."

packages/wrangler/src/cli.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import process from "process";
22
import { hideBin } from "yargs/helpers";
3+
import { FatalError } from "./errors";
34
import { reportError, initReporting } from "./reporting";
45
import { main } from ".";
56

@@ -11,9 +12,10 @@ process.on("uncaughtExceptionMonitor", async (err, origin) => {
1112
await reportError(err, origin);
1213
});
1314

14-
main(hideBin(process.argv)).catch(() => {
15+
main(hideBin(process.argv)).catch((e) => {
1516
// The logging of any error that was thrown from `main()` is handled in the `yargs.fail()` handler.
1617
// Here we just want to ensure that the process exits with a non-zero code.
1718
// We don't want to do this inside the `main()` function, since that would kill the process when running our tests.
18-
process.exit(1);
19+
const exitCode = (e instanceof FatalError && e.code) || 1;
20+
process.exit(exitCode);
1921
});

packages/wrangler/src/errors.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export class DeprecationError extends Error {
2+
constructor(message: string) {
3+
super(`DEPRECATION:\n${message}`);
4+
}
5+
}
6+
7+
export class FatalError extends Error {
8+
constructor(message?: string, readonly code?: number) {
9+
super(message);
10+
}
11+
}

packages/wrangler/src/index.tsx

+1-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { createWorkerUploadForm } from "./create-worker-upload-form";
1818
import Dev from "./dev/dev";
1919
import { confirm, prompt } from "./dialogs";
2020
import { getEntry } from "./entry";
21+
import { DeprecationError } from "./errors";
2122
import {
2223
getNamespaceId,
2324
listNamespaces,
@@ -211,11 +212,6 @@ function demandOneOfOption(...options: string[]) {
211212
}
212213

213214
class CommandLineArgsError extends Error {}
214-
class DeprecationError extends Error {
215-
constructor(message: string) {
216-
super(`DEPRECATION WARNING:\n${message}`);
217-
}
218-
}
219215

220216
export async function main(argv: string[]): Promise<void> {
221217
const wrangler = makeCLI(argv)

packages/wrangler/src/pages.tsx

+22-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getType } from "mime";
1010
import { buildWorker } from "../pages/functions/buildWorker";
1111
import { generateConfigFromFileTree } from "../pages/functions/filepath-routing";
1212
import { writeRoutesModule } from "../pages/functions/routes";
13+
import { FatalError } from "./errors";
1314
import openInBrowser from "./open-in-browser";
1415
import { toUrlPath } from "./paths";
1516
import type { Config } from "../pages/functions/routes";
@@ -25,17 +26,20 @@ import type { BuilderCallback } from "yargs";
2526
export const pagesBetaWarning =
2627
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
2728

28-
const EXIT_CALLBACKS: (() => void)[] = [];
29-
const EXIT = (message?: string, code?: number) => {
30-
if (message) console.log(message);
31-
if (code) process.exitCode = code;
32-
EXIT_CALLBACKS.forEach((callback) => callback());
29+
const CLEANUP_CALLBACKS: (() => void)[] = [];
30+
const CLEANUP = () => {
31+
CLEANUP_CALLBACKS.forEach((callback) => callback());
3332
RUNNING_BUILDERS.forEach((builder) => builder.stop?.());
34-
process.exit(code);
3533
};
3634

37-
process.on("SIGINT", () => EXIT());
38-
process.on("SIGTERM", () => EXIT());
35+
process.on("SIGINT", () => {
36+
CLEANUP();
37+
process.exit();
38+
});
39+
process.on("SIGTERM", () => {
40+
CLEANUP();
41+
process.exit();
42+
});
3943

4044
function isWindows() {
4145
return process.platform === "win32";
@@ -108,11 +112,13 @@ async function spawnProxyProcess({
108112
port?: number;
109113
command: (string | number)[];
110114
}): Promise<void | number> {
111-
if (command.length === 0)
112-
return EXIT(
115+
if (command.length === 0) {
116+
CLEANUP();
117+
throw new FatalError(
113118
"Must specify a directory of static assets to serve or a command to run.",
114119
1
115120
);
121+
}
116122

117123
console.log(`Running ${command.join(" ")}...`);
118124
const proxy = spawn(
@@ -126,7 +132,7 @@ async function spawnProxyProcess({
126132
},
127133
}
128134
);
129-
EXIT_CALLBACKS.push(() => {
135+
CLEANUP_CALLBACKS.push(() => {
130136
proxy.kill();
131137
});
132138

@@ -157,7 +163,8 @@ async function spawnProxyProcess({
157163
.filter((port) => port !== undefined)[0];
158164

159165
if (port === undefined) {
160-
return EXIT(
166+
CLEANUP();
167+
throw new FatalError(
161168
"Could not automatically determine proxy port. Please specify the proxy port with --proxy.",
162169
1
163170
);
@@ -970,13 +977,14 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
970977
});
971978
}
972979

973-
EXIT_CALLBACKS.push(() => {
980+
CLEANUP_CALLBACKS.push(() => {
974981
server.close();
975982
miniflare.dispose().catch((err) => miniflare.log.error(err));
976983
});
977984
} catch (e) {
978985
miniflare.log.error(e as Error);
979-
EXIT("Could not start Miniflare.", 1);
986+
CLEANUP();
987+
throw new FatalError("Could not start Miniflare.", 1);
980988
}
981989
}
982990
)

0 commit comments

Comments
 (0)