mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Support NO_COLOR environment variable (#3055)
This commit is contained in:
@@ -14,10 +14,34 @@ If both a global and local `bunfig` are detected, the results are shallow-merged
|
||||
|
||||
## Environment variables
|
||||
|
||||
<!-- - `GOMAXPROCS`: For `bun bun`, this sets the maximum number of threads to use. If you’re experiencing an issue with `bun bun`, try setting `GOMAXPROCS=1` to force Bun to run single-threaded -->
|
||||
These environment variables are checked by Bun to detect functionality and toggle features.
|
||||
|
||||
- `DISABLE_BUN_ANALYTICS=1` this disables Bun's analytics. Bun records bundle timings (so we can answer with data, "is Bun getting faster?") and feature usage (e.g., "are people actually using macros?"). The request body size is about 60 bytes, so it’s not a lot of data
|
||||
- `TMPDIR`: Bun occasionally requires a directory to store intermediate assets during bundling or other operations. If unset, `TMPDIR` defaults to the platform-specific temporary directory (on Linux, `/tmp` and on macOS `/private/tmp`).
|
||||
{% table %}
|
||||
|
||||
- Name
|
||||
- Description
|
||||
|
||||
---
|
||||
|
||||
- `TMPDIR`
|
||||
- Bun occasionally requires a directory to store intermediate assets during bundling or other operations. If unset, defaults to the platform-specific temporary directory: `/tmp` on Linux, `/private/tmp` on macOS.
|
||||
|
||||
---
|
||||
|
||||
- `NO_COLOR`
|
||||
- If `NO_COLOR=1`, then ANSI color output is [disabled](https://no-color.org/).
|
||||
|
||||
---
|
||||
|
||||
- `FORCE_COLOR`
|
||||
- If `FORCE_COLOR=1`, then ANSI color output is force enabled, even if `NO_COLOR` is set.
|
||||
|
||||
---
|
||||
|
||||
- `DO_NOT_TRACK`
|
||||
- If `DO_NOT_TRACK=1`, then analytics are [disabled](https://do-not-track.dev/). Bun records bundle timings (so we can answer with data, "is Bun getting faster?") and feature usage (e.g., "are people actually using macros?"). The request body size is about 60 bytes, so it's not a lot of data.
|
||||
|
||||
{% /table %}
|
||||
|
||||
## Runtime
|
||||
|
||||
|
||||
@@ -532,8 +532,9 @@ pub const Bundler = struct {
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (this.env.map.get("DISABLE_BUN_ANALYTICS")) |should_disable| {
|
||||
if (strings.eqlComptime(should_disable, "1")) {
|
||||
if (this.env.map.get("DO_NOT_TRACK")) |dnt| {
|
||||
// https://do-not-track.dev/
|
||||
if (strings.eqlComptime(dnt, "1")) {
|
||||
Analytics.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,29 +95,32 @@ pub const Source = struct {
|
||||
}
|
||||
|
||||
pub fn isNoColor() bool {
|
||||
return bun.getenvZ("NO_COLOR") != null;
|
||||
const no_color = bun.getenvZ("NO_COLOR") orelse return false;
|
||||
// https://no-color.org/
|
||||
// "when present and not an empty string (regardless of its value)"
|
||||
return no_color.len != 0;
|
||||
}
|
||||
|
||||
pub fn isForceColor() ?bool {
|
||||
const force_color_str = bun.getenvZ("FORCE_COLOR") orelse return null;
|
||||
return force_color_str.len == 0 or
|
||||
strings.eqlComptime(force_color_str, "TRUE") or
|
||||
strings.eqlComptime(force_color_str, "ON") or
|
||||
strings.eqlComptime(force_color_str, "YES") or
|
||||
strings.eqlComptime(force_color_str, "1") or
|
||||
strings.eqlComptime(force_color_str, " ");
|
||||
pub fn isForceColor() bool {
|
||||
const force_color = bun.getenvZ("FORCE_COLOR") orelse return false;
|
||||
// Supported by Node.js, if set will ignore NO_COLOR.
|
||||
// - "1", "true", or "" to indicate 16-color support
|
||||
// - "2" to indicate 256-color support
|
||||
// - "3" to indicate 16 million-color support
|
||||
return force_color.len == 0 or
|
||||
strings.eqlComptime(force_color, "1") or
|
||||
strings.eqlComptime(force_color, "true") or
|
||||
strings.eqlComptime(force_color, "2") or
|
||||
strings.eqlComptime(force_color, "3");
|
||||
}
|
||||
|
||||
pub fn isColorTerminal() bool {
|
||||
if (isForceColor()) |val| return val;
|
||||
if (bun.getenvZ("COLORTERM")) |color_term| return !strings.eqlComptime(color_term, "0");
|
||||
|
||||
if (bun.getenvZ("TERM")) |term| {
|
||||
if (strings.eqlComptime(term, "dumb")) return false;
|
||||
|
||||
return true;
|
||||
if (bun.getenvZ("COLORTERM")) |color_term| {
|
||||
return !strings.eqlComptime(color_term, "0");
|
||||
}
|
||||
if (bun.getenvZ("TERM")) |term| {
|
||||
return !strings.eqlComptime(term, "dumb");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -128,27 +131,26 @@ pub const Source = struct {
|
||||
if (!stdout_stream_set) {
|
||||
stdout_stream_set = true;
|
||||
if (comptime Environment.isNative) {
|
||||
var is_color_terminal: ?bool = null;
|
||||
if (_source.stream.isTty()) {
|
||||
var enable_color: ?bool = null;
|
||||
if (isForceColor()) {
|
||||
enable_color = true;
|
||||
} else if (isNoColor() or !isColorTerminal()) {
|
||||
enable_color = false;
|
||||
}
|
||||
|
||||
const is_stdout_tty = _source.stream.isTty();
|
||||
if (is_stdout_tty) {
|
||||
stdout_descriptor_type = OutputStreamDescriptor.terminal;
|
||||
enable_ansi_colors_stdout = isColorTerminal();
|
||||
is_color_terminal = enable_ansi_colors_stdout;
|
||||
} else if (isForceColor()) |val| {
|
||||
enable_ansi_colors_stdout = val;
|
||||
} else {
|
||||
enable_ansi_colors_stdout = false;
|
||||
}
|
||||
|
||||
if (_source.error_stream.isTty()) {
|
||||
const is_stderr_tty = _source.error_stream.isTty();
|
||||
if (is_stderr_tty) {
|
||||
stderr_descriptor_type = OutputStreamDescriptor.terminal;
|
||||
enable_ansi_colors_stderr = is_color_terminal orelse isColorTerminal();
|
||||
} else if (isForceColor()) |val| {
|
||||
enable_ansi_colors_stderr = val;
|
||||
} else {
|
||||
enable_ansi_colors_stderr = false;
|
||||
}
|
||||
|
||||
enable_ansi_colors = enable_ansi_colors_stderr or enable_ansi_colors_stdout;
|
||||
enable_ansi_colors_stdout = enable_color orelse is_stdout_tty;
|
||||
enable_ansi_colors_stderr = enable_color orelse is_stderr_tty;
|
||||
enable_ansi_colors = enable_ansi_colors_stdout or enable_ansi_colors_stderr;
|
||||
}
|
||||
|
||||
stdout_stream = _source.stream;
|
||||
|
||||
35
test/cli/bun.test.ts
Normal file
35
test/cli/bun.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { spawnSync } from "bun";
|
||||
import { bunExe } from "harness";
|
||||
|
||||
describe("bun", () => {
|
||||
describe("NO_COLOR", () => {
|
||||
for (const value of ["1", "0", "foo", " "]) {
|
||||
test(`respects NO_COLOR=${JSON.stringify(value)} to disable color`, () => {
|
||||
const { stdout } = spawnSync({
|
||||
cmd: [bunExe()],
|
||||
env: {
|
||||
NO_COLOR: value,
|
||||
},
|
||||
});
|
||||
expect(stdout.toString()).not.toMatch(/\u001b\[\d+m/);
|
||||
});
|
||||
}
|
||||
for (const value of ["", undefined]) {
|
||||
// TODO: need a way to fake a tty in order to test this,
|
||||
// and cannot use FORCE_COLOR since that will always override NO_COLOR.
|
||||
test.todo(`respects NO_COLOR=${JSON.stringify(value)} to enable color`, () => {
|
||||
const { stdout } = spawnSync({
|
||||
cmd: [bunExe()],
|
||||
env:
|
||||
value === undefined
|
||||
? {}
|
||||
: {
|
||||
NO_COLOR: value,
|
||||
},
|
||||
});
|
||||
expect(stdout.toString()).toMatch(/\u001b\[\d+m/);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user