Skip to content

Commit 56b8597

Browse files
thomasballingerConvex, Inc.
authored and
Convex, Inc.
committed
Avoid console.error in CLI (#27657)
Recent versions of Node.js [print console.error() in red](nodejs/node#51629), changing the color of convex CLI output unintentionally. Change everywhere we `console.error` as a means to write to stderr to `process.stderr.write` calls. GitOrigin-RevId: 134e5302858c309e3a7fd9d470a685d9bf60b1df
1 parent a64427e commit 56b8597

File tree

4 files changed

+31
-21
lines changed

4 files changed

+31
-21
lines changed

npm-packages/convex/src/bundler/context.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as Sentry from "@sentry/node";
22
import chalk from "chalk";
33
import ora, { Ora } from "ora";
44
import { Filesystem, nodeFs } from "./fs.js";
5+
import { format } from "util";
56

67
// How the error should be handled when running `npx convex dev`.
78
export type ErrorType =
@@ -56,22 +57,27 @@ async function flushAndExit(exitCode: number, err?: any) {
5657
return process.exit(exitCode);
5758
}
5859

60+
// console.error before it started being red by default in Node v20
61+
function logToStderr(...args: unknown[]) {
62+
process.stderr.write(`${format(...args)}\n`);
63+
}
64+
5965
// Handles clearing spinner so that it doesn't get messed up
6066
export function logError(ctx: Context, message: string) {
6167
ctx.spinner?.clear();
62-
console.error(message);
68+
logToStderr(message);
6369
}
6470

6571
// Handles clearing spinner so that it doesn't get messed up
6672
export function logWarning(ctx: Context, message: string) {
6773
ctx.spinner?.clear();
68-
console.error(message);
74+
logToStderr(message);
6975
}
7076

7177
// Handles clearing spinner so that it doesn't get messed up
7278
export function logMessage(ctx: Context, ...logged: any) {
7379
ctx.spinner?.clear();
74-
console.error(...logged);
80+
logToStderr(...logged);
7581
}
7682

7783
// For the rare case writing output to stdout. Status and error messages
@@ -105,7 +111,7 @@ export function changeSpinner(ctx: Context, message: string) {
105111
// Add newline to prevent clobbering
106112
ctx.spinner.text = message + "\n";
107113
} else {
108-
console.error(message);
114+
logToStderr(message);
109115
}
110116
}
111117

@@ -114,7 +120,7 @@ export function logFailure(ctx: Context, message: string) {
114120
ctx.spinner.fail(message);
115121
ctx.spinner = undefined;
116122
} else {
117-
console.error(`${chalk.red(`✖`)} ${message}`);
123+
logToStderr(`${chalk.red(`✖`)} ${message}`);
118124
}
119125
}
120126

@@ -124,7 +130,7 @@ export function logFinishedStep(ctx: Context, message: string) {
124130
ctx.spinner.succeed(message);
125131
ctx.spinner = undefined;
126132
} else {
127-
console.error(`${chalk.green(`✔`)} ${message}`);
133+
logToStderr(`${chalk.green(`✔`)} ${message}`);
128134
}
129135
}
130136

npm-packages/convex/src/bundler/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,23 @@ test("returns true when multiple imports and httpRouter is imported", async () =
115115

116116
test("bundle warns about https.js|ts at top level", async () => {
117117
const fixtureDir = dirname + "/test_fixtures/js/project_with_https";
118-
const logSpy = vi.spyOn(console, "error");
118+
const logSpy = vi.spyOn(process.stderr, "write");
119119
await entryPoints(oneoffContext, fixtureDir, false);
120120
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("https"));
121121
});
122122

123123
test("bundle does not warn about https.js|ts which is not at top level", async () => {
124124
const fixtureDir =
125125
dirname + "/test_fixtures/js/project_with_https_not_at_top_level";
126-
const logSpy = vi.spyOn(console, "error");
126+
const logSpy = vi.spyOn(process.stderr, "write");
127127
await entryPoints(oneoffContext, fixtureDir, false);
128128
expect(logSpy).toHaveBeenCalledTimes(0);
129129
});
130130

131131
test("bundle does not warn about https.js|ts which does not import httpRouter", async () => {
132132
const fixtureDir =
133133
dirname + "/test_fixtures/js/project_with_https_without_router";
134-
const logSpy = vi.spyOn(console, "error");
134+
const logSpy = vi.spyOn(process.stderr, "write");
135135
await entryPoints(oneoffContext, fixtureDir, false);
136136
expect(logSpy).toHaveBeenCalledTimes(0);
137137
});

npm-packages/convex/src/cli/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env node
21
/* eslint-disable no-restricted-syntax */
32
import { Command } from "@commander-js/extra-typings";
43
import { init } from "./init.js";
@@ -29,10 +28,16 @@ import { env } from "./env.js";
2928
import { data } from "./data.js";
3029
import inquirer from "inquirer";
3130
import inquirerSearchList from "inquirer-search-list";
31+
import { format } from "util";
3232

3333
const MINIMUM_MAJOR_VERSION = 16;
3434
const MINIMUM_MINOR_VERSION = 15;
3535

36+
// console.error before it started being red by default in Node.js v20
37+
function logToStderr(...args: unknown[]) {
38+
process.stderr.write(`${format(...args)}\n`);
39+
}
40+
3641
async function main() {
3742
// If you want to use `@sentry/tracing` in your project directly, use a named import instead:
3843
// import * as SentryTracing from "@sentry/tracing"
@@ -63,25 +68,25 @@ async function main() {
6368
(majorVersion === MINIMUM_MAJOR_VERSION &&
6469
minorVersion < MINIMUM_MINOR_VERSION)
6570
) {
66-
console.error(
71+
logToStderr(
6772
chalk.red(
6873
`Your Node version ${nodeVersion} is too old. Convex requires at least Node v${MINIMUM_MAJOR_VERSION}.${MINIMUM_MINOR_VERSION}`,
6974
),
7075
);
71-
console.error(
76+
logToStderr(
7277
chalk.gray(
7378
`You can use ${chalk.bold(
7479
"nvm",
7580
)} (https://github.com/nvm-sh/nvm#installing-and-updating) to manage different versions of Node.`,
7681
),
7782
);
78-
console.error(
83+
logToStderr(
7984
chalk.gray(
8085
"After installing `nvm`, install the latest version of Node with " +
8186
chalk.bold("`nvm install node`."),
8287
),
8388
);
84-
console.error(
89+
logToStderr(
8590
chalk.gray(
8691
"Then, activate the installed version in your terminal with " +
8792
chalk.bold("`nvm use`."),

npm-packages/convex/src/cli/lib/config.test.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@ test("parseProjectConfig", async () => {
1111
throw new Error();
1212
},
1313
};
14-
const consoleSpy = vi
15-
.spyOn(global.console, "error")
16-
.mockImplementation(() => {
17-
// Do nothing
18-
});
14+
const stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation(() => {
15+
// Do nothing
16+
return true;
17+
});
1918
const assertParses = async (inp: any) => {
2019
expect(await parseProjectConfig(ctx, inp)).toEqual(inp);
2120
};
2221
const assertParseError = async (inp: any, err: string) => {
2322
await expect(parseProjectConfig(ctx, inp)).rejects.toThrow();
24-
expect(consoleSpy).toHaveBeenCalledWith(err);
23+
expect(stderrSpy).toHaveBeenCalledWith(err);
2524
};
2625

2726
await assertParses({
@@ -60,6 +59,6 @@ test("parseProjectConfig", async () => {
6059
functions: "functions/",
6160
authInfo: [{}],
6261
},
63-
"Expected `authInfo` in `convex.json` to be of type AuthInfo[]",
62+
"Expected `authInfo` in `convex.json` to be of type AuthInfo[]\n",
6463
);
6564
});

0 commit comments

Comments
 (0)