-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathplain-detect.ts
More file actions
94 lines (87 loc) · 3.74 KB
/
plain-detect.ts
File metadata and controls
94 lines (87 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* Plain-output detection and ANSI stripping utilities.
*
* Extracted to its own module to avoid circular dependencies between
* `markdown.ts` (which imports from `colors.ts`) and `colors.ts`
* (which needs `isPlainOutput()` to gate terminal hyperlinks).
*
* ## Output mode resolution (highest → lowest priority)
*
* 1. `SENTRY_PLAIN_OUTPUT=1` → plain
* 2. `SENTRY_PLAIN_OUTPUT=0` → rendered (force rich, even when piped)
* 3. `NO_COLOR` (any non-empty value) → plain
* 4. `FORCE_COLOR=0` → plain (only when stdout is a TTY)
* 5. `FORCE_COLOR=1` on a TTY → rendered
* 6. `!process.stdout.isTTY` → plain
* 7. default (TTY, no overrides) → rendered
*/
import { getEnv } from "../env.js";
/**
* Returns true if an env var value should be treated as "truthy" for
* purposes of enabling/disabling output modes.
*
* Falsy values: `"0"`, `"false"`, `""` (case-insensitive).
* Everything else (e.g. `"1"`, `"true"`, `"yes"`) is truthy.
*/
function isTruthyEnv(val: string): boolean {
const normalized = val.toLowerCase().trim();
return normalized !== "0" && normalized !== "false" && normalized !== "";
}
/**
* Determines whether output should be plain (no ANSI codes, no raw
* markdown syntax).
*
* Evaluated fresh on each call so tests can flip env vars between assertions
* and changes to `process.stdout.isTTY` are picked up immediately.
*
* Priority (highest first):
* 1. `SENTRY_PLAIN_OUTPUT` — explicit project-specific override (custom
* semantics: `"0"` / `"false"` / `""` force color on)
* 2. `NO_COLOR` — follows the no-color.org spec: any **non-empty** value
* disables color, regardless of its content (including `"0"` / `"false"`)
* 3. `FORCE_COLOR` — follows chalk/supports-color convention, but only
* applies to interactive terminals. When stdout is piped, FORCE_COLOR
* is ignored so that `cmd | less` always produces clean output.
* Users who truly want color in pipes can use `SENTRY_PLAIN_OUTPUT=0`.
* 4. `process.stdout.isTTY` — auto-detect interactive terminal
*/
export function isPlainOutput(): boolean {
const plain = getEnv().SENTRY_PLAIN_OUTPUT;
if (plain !== undefined) {
return isTruthyEnv(plain);
}
// no-color.org spec: presence of a non-empty value disables color.
// Unlike SENTRY_PLAIN_OUTPUT, "0" and "false" still mean "disable color".
const noColor = getEnv().NO_COLOR;
if (noColor !== undefined) {
return noColor !== "";
}
// FORCE_COLOR only applies to interactive terminals. When stdout is
// piped/redirected, FORCE_COLOR is ignored so that `cmd | less` always
// produces clean output without ANSI codes.
const forceColor = getEnv().FORCE_COLOR;
if (process.stdout.isTTY && forceColor !== undefined && forceColor !== "") {
return forceColor === "0";
}
return !process.stdout.isTTY;
}
/**
* Strip ANSI escape sequences from a string.
*
* Handles all CSI sequences (`\x1b[...LETTER` — covers SGR colour codes,
* cursor movement, screen-clear, and other control sequences) and OSC 8
* terminal hyperlink sequences (`\x1b]8;;url\x07text\x1b]8;;\x07`).
*/
export function stripAnsi(text: string): string {
return (
text
// Full CSI spec: \x1b[ + parameter bytes (0x30-0x3F) + intermediate
// bytes (0x20-0x2F, e.g. space, !, ") + final byte (0x40-0x7E).
// The narrower [0-9;?]*[A-Za-z] missed sequences with intermediate
// bytes like \x1b[!p (Soft Terminal Reset) or \x1b[1 q (cursor style).
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape detection requires matching \x1b
.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g, "")
// biome-ignore lint/suspicious/noControlCharactersInRegex: OSC 8 hyperlink sequences use \x1b and \x07
.replace(/\x1b\]8;;[^\x07]*\x07/g, "")
);
}