@mpen/ansi-colors 0.1.0

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/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # @mpen/ansi-colors
2
+
3
+ A lightweight, high-performance, and feature-rich terminal coloring library for Node.js, Bun, and other JS environments.
4
+
5
+ Derived from the extremely fast and optimized core of `picocolors`, this library extends it with full **Truecolor (
6
+ 24-bit RGB)** support, **Hexadecimal** color formatting, and clean TypeScript typings.
7
+
8
+ ## Features
9
+
10
+ - **Zero dependencies** — extremely small footprint.
11
+ - **Truecolor Support** — format terminal text using arbitrary 24-bit RGB and Hex colors.
12
+ - **High Performance** — builds on `picocolors`' highly optimized formatter architecture.
13
+ - **Auto-detection** — automatically detects color support (respects `FORCE_COLOR`, `NO_COLOR`, and `CI`).
14
+ - **Nestable & Composable** — mix and match formatting, text colors, and background colors seamlessly.
15
+ - **Fully Typed** — clean TypeScript declarations out of the box.
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```sh
22
+ bun add @mpen/ansi-colors
23
+ ```
24
+
25
+ ```sh
26
+ npm install @mpen/ansi-colors
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Usage
32
+
33
+ ### Basic Usage
34
+
35
+ ```ts
36
+ import { createColors } from '@mpen/ansi-colors'
37
+
38
+ const c = createColors()
39
+
40
+ console.log(c.green('Success! Operation completed.'))
41
+ console.log(c.bold(c.red('Error: Access denied.')))
42
+ ```
43
+
44
+ ### Truecolor (RGB and Hex)
45
+
46
+ You can define custom formatters using exact 24-bit color values:
47
+
48
+ ```ts
49
+ import { createColors } from '@mpen/ansi-colors'
50
+
51
+ const c = createColors()
52
+
53
+ // Hex Colors
54
+ const purple = c.hex('#7c3aed')
55
+ const bgGold = c.bgHex('ffd700') // '#' prefix is optional
56
+ console.log(purple(bgGold(' Royal and Gold ')))
57
+
58
+ // RGB Colors
59
+ const orange = c.rgb(255, 128, 0)
60
+ const bgTeal = c.bgRgb(0, 128, 128)
61
+ console.log(orange(bgTeal(' Sunset Teal ')))
62
+ ```
63
+
64
+ ### Composing and Nesting
65
+
66
+ Formatters are functions that can be nested and composed inside one another:
67
+
68
+ ```ts
69
+ import { createColors } from '@mpen/ansi-colors'
70
+
71
+ const c = createColors()
72
+
73
+ console.log(c.bold(`Underline ${c.underline('and')} green ${c.green('text')}`))
74
+ ```
75
+
76
+ ### Disabling Colors Dynamically
77
+
78
+ By default, `createColors()` guesses support for colors automatically. You can explicitly force or disable colors by
79
+ passing a boolean:
80
+
81
+ ```ts
82
+ import { createColors } from '@mpen/ansi-colors'
83
+
84
+ // Force colors disabled (useful for raw logs)
85
+ const c = createColors(false)
86
+
87
+ console.log(c.red('This will print as plain uncolored text.'))
88
+ ```
89
+
90
+ ---
91
+
92
+ ### Color Support Auto-Detection
93
+
94
+ By default, `createColors()` automatically detects whether the terminal environment supports ANSI colors. The automatic detection evaluates:
95
+
96
+ - **`NO_COLOR`**: If this environment variable is set (regardless of its value), colors are disabled (see [no-color.org](https://no-color.org/)).
97
+ - **`FORCE_COLOR`**: If set, color support is forced-enabled.
98
+ - **`CI`**: If set (typically in continuous integration pipelines), colors are enabled.
99
+ - **`TERM`**: If set to `dumb`, colors are disabled.
100
+ - **TTY Check**: Colors are enabled if `process.stdout.isTTY` is truthy.
101
+ - **Windows**: On Windows environments (`win32` platform), colors are enabled.
102
+
103
+ ---
104
+
105
+ ## API Reference
106
+
107
+ ### `createColors(enabled?: boolean)`
108
+
109
+ Creates a new colors instance. By default, it detects color support using environment variables.
110
+
111
+ #### Instance Properties & Formatters
112
+
113
+ | Category | Formatters |
114
+ | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
115
+ | **Modifiers** | `reset`, `bold`, `dim`, `italic`, `underline`, `inverse`, `hidden`, `strikethrough` |
116
+ | **Text Colors** | `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray` / `blackBright` |
117
+ | **Bright Text** | `redBright`, `greenBright`, `yellowBright`, `blueBright`, `magentaBright`, `cyanBright`, `whiteBright` |
118
+ | **Backgrounds** | `bgBlack`, `bgRed`, `bgGreen`, `bgYellow`, `bgBlue`, `bgMagenta`, `bgCyan`, `bgWhite` |
119
+ | **Bright BG** | `bgBlackBright`, `bgRedBright`, `bgGreenBright`, `bgYellowBright`, `bgBlueBright`, `bgMagentaBright`, `bgCyanBright`, `bgWhiteBright` |
120
+
121
+ #### Instance Custom Factories
122
+
123
+ - **`rgb(r, g, b)`**: Returns a text color formatter for the given RGB channels (`0-255`).
124
+ - **`bgRgb(r, g, b)`**: Returns a background color formatter for the given RGB channels (`0-255`).
125
+ - **`hex(color)`**: Returns a text color formatter for the given Hex string (e.g., `#7c3aed` or `7c3aed`).
126
+ - **`bgHex(color)`**: Returns a background color formatter for the given Hex string.
127
+
128
+ ---
129
+
130
+ ## License
131
+
132
+ MIT © Mark Penner. Portions derived from [picocolors](https://github.com/alexeyraspopov/picocolors) (Copyright
133
+ © Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov).
@@ -0,0 +1,111 @@
1
+ //#region src/color.d.ts
2
+ /**
3
+ * Formats a value with ANSI escape sequences.
4
+ *
5
+ * @param input - The value to convert to a string and format.
6
+ * @returns The formatted string.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const red: ColorFormatter = (input) => `\x1b[31m${input}\x1b[39m`
11
+ * red('error')
12
+ * ```
13
+ */
14
+ type ColorFormatter = (input: unknown) => string;
15
+ /**
16
+ * Creates a formatter for a 24-bit RGB ANSI color.
17
+ *
18
+ * @param red - The red channel as an integer from 0 to 255.
19
+ * @param green - The green channel as an integer from 0 to 255.
20
+ * @param blue - The blue channel as an integer from 0 to 255.
21
+ * @returns A formatter for the requested RGB color.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const colors = createColors()
26
+ * const orange = colors.rgb(255, 128, 0)
27
+ * orange('warning')
28
+ * ```
29
+ */
30
+ type RgbColorFactory = (red: number, green: number, blue: number) => ColorFormatter;
31
+ /**
32
+ * Creates a formatter for a 24-bit hexadecimal ANSI color.
33
+ *
34
+ * @param color - The color as `#rgb`, `rgb`, `#rrggbb`, or `rrggbb`.
35
+ * @returns A formatter for the requested hexadecimal color.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const colors = createColors()
40
+ * const violet = colors.hex('#7c3aed')
41
+ * violet('accent')
42
+ * ```
43
+ */
44
+ type HexColorFactory = (color: string) => ColorFormatter;
45
+ type RgbFormatterFactory = (red: number, green: number, blue: number) => ColorFormatter;
46
+ /**
47
+ * Creates a color formatter set.
48
+ *
49
+ * @param enabled - Whether the returned formatters should emit ANSI escape sequences.
50
+ * Defaults to detected color support for the current process.
51
+ * @returns A color formatter set.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import createColors from '@mpen/ansi-colors'
56
+ *
57
+ * const pc = createColors(true)
58
+ * pc.bold(pc.red('error'))
59
+ * ```
60
+ */
61
+ declare function createColors(enabled?: boolean): {
62
+ isColorSupported: boolean;
63
+ reset: ColorFormatter;
64
+ bold: ColorFormatter;
65
+ dim: ColorFormatter;
66
+ italic: ColorFormatter;
67
+ underline: ColorFormatter;
68
+ inverse: ColorFormatter;
69
+ hidden: ColorFormatter;
70
+ strikethrough: ColorFormatter;
71
+ black: ColorFormatter;
72
+ red: ColorFormatter;
73
+ green: ColorFormatter;
74
+ yellow: ColorFormatter;
75
+ blue: ColorFormatter;
76
+ magenta: ColorFormatter;
77
+ cyan: ColorFormatter;
78
+ white: ColorFormatter;
79
+ gray: ColorFormatter;
80
+ bgBlack: ColorFormatter;
81
+ bgRed: ColorFormatter;
82
+ bgGreen: ColorFormatter;
83
+ bgYellow: ColorFormatter;
84
+ bgBlue: ColorFormatter;
85
+ bgMagenta: ColorFormatter;
86
+ bgCyan: ColorFormatter;
87
+ bgWhite: ColorFormatter;
88
+ blackBright: ColorFormatter;
89
+ redBright: ColorFormatter;
90
+ greenBright: ColorFormatter;
91
+ yellowBright: ColorFormatter;
92
+ blueBright: ColorFormatter;
93
+ magentaBright: ColorFormatter;
94
+ cyanBright: ColorFormatter;
95
+ whiteBright: ColorFormatter;
96
+ bgBlackBright: ColorFormatter;
97
+ bgRedBright: ColorFormatter;
98
+ bgGreenBright: ColorFormatter;
99
+ bgYellowBright: ColorFormatter;
100
+ bgBlueBright: ColorFormatter;
101
+ bgMagentaBright: ColorFormatter;
102
+ bgCyanBright: ColorFormatter;
103
+ bgWhiteBright: ColorFormatter;
104
+ rgb: RgbFormatterFactory;
105
+ bgRgb: RgbFormatterFactory;
106
+ hex: (color: string) => ColorFormatter;
107
+ bgHex: (color: string) => ColorFormatter;
108
+ };
109
+ type Colors = ReturnType<typeof createColors>;
110
+ //#endregion
111
+ export { ColorFormatter, Colors, HexColorFactory, RgbColorFactory, createColors, createColors as default };
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ //#region src/color.ts
2
+ const processInfo = typeof process === "undefined" ? void 0 : process;
3
+ const env = processInfo?.env ?? {};
4
+ /**
5
+ * Whether ANSI colors are supported in the current process.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import createColors from '@mpen/ansi-colors'
10
+ *
11
+ * const colors = createColors()
12
+ * if (colors.isColorSupported) {
13
+ * console.log('colors are enabled')
14
+ * }
15
+ * ```
16
+ */
17
+ const isColorSupported = !env.NO_COLOR && (Boolean(env.FORCE_COLOR) || processInfo?.platform === "win32" || Boolean(processInfo?.stdout?.isTTY) && env.TERM !== "dumb" || Boolean(env.CI));
18
+ const replaceClose = (string, close, replace, index) => {
19
+ let result = "";
20
+ let cursor = 0;
21
+ do {
22
+ result += string.substring(cursor, index) + replace;
23
+ cursor = index + close.length;
24
+ index = string.indexOf(close, cursor);
25
+ } while (index >= 0);
26
+ return result + string.substring(cursor);
27
+ };
28
+ const formatter = (open, close, replace = open) => (input) => {
29
+ const string = String(input);
30
+ const index = string.indexOf(close, open.length);
31
+ return index >= 0 ? open + replaceClose(string, close, replace, index) + close : open + string + close;
32
+ };
33
+ const assertRgbChannel = (channel, name) => {
34
+ if (!Number.isInteger(channel) || channel < 0 || channel > 255) throw new RangeError(`${name} must be an integer from 0 to 255`);
35
+ };
36
+ const rgbFormatter = (f, prefix, close) => (red, green, blue) => {
37
+ assertRgbChannel(red, "red");
38
+ assertRgbChannel(green, "green");
39
+ assertRgbChannel(blue, "blue");
40
+ return f(`\x1b[${prefix};2;${red};${green};${blue}m`, close);
41
+ };
42
+ const parseHexColor = (color) => {
43
+ const hex = /^#?(?<hex>[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color)?.groups?.hex;
44
+ if (!hex) throw new TypeError("color must be a 3- or 6-digit hex color");
45
+ if (hex.length === 3) return [
46
+ Number.parseInt(`${hex[0]}${hex[0]}`, 16),
47
+ Number.parseInt(`${hex[1]}${hex[1]}`, 16),
48
+ Number.parseInt(`${hex[2]}${hex[2]}`, 16)
49
+ ];
50
+ return [
51
+ Number.parseInt(hex.slice(0, 2), 16),
52
+ Number.parseInt(hex.slice(2, 4), 16),
53
+ Number.parseInt(hex.slice(4, 6), 16)
54
+ ];
55
+ };
56
+ /**
57
+ * Creates a color formatter set.
58
+ *
59
+ * @param enabled - Whether the returned formatters should emit ANSI escape sequences.
60
+ * Defaults to detected color support for the current process.
61
+ * @returns A color formatter set.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import createColors from '@mpen/ansi-colors'
66
+ *
67
+ * const pc = createColors(true)
68
+ * pc.bold(pc.red('error'))
69
+ * ```
70
+ */
71
+ function createColors(enabled = isColorSupported) {
72
+ const f = enabled ? formatter : () => String;
73
+ const rgb = rgbFormatter(f, 38, "\x1B[39m");
74
+ const bgRgb = rgbFormatter(f, 48, "\x1B[49m");
75
+ return {
76
+ isColorSupported: enabled,
77
+ reset: f("\x1B[0m", "\x1B[0m"),
78
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
79
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
80
+ italic: f("\x1B[3m", "\x1B[23m"),
81
+ underline: f("\x1B[4m", "\x1B[24m"),
82
+ inverse: f("\x1B[7m", "\x1B[27m"),
83
+ hidden: f("\x1B[8m", "\x1B[28m"),
84
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
85
+ black: f("\x1B[30m", "\x1B[39m"),
86
+ red: f("\x1B[31m", "\x1B[39m"),
87
+ green: f("\x1B[32m", "\x1B[39m"),
88
+ yellow: f("\x1B[33m", "\x1B[39m"),
89
+ blue: f("\x1B[34m", "\x1B[39m"),
90
+ magenta: f("\x1B[35m", "\x1B[39m"),
91
+ cyan: f("\x1B[36m", "\x1B[39m"),
92
+ white: f("\x1B[37m", "\x1B[39m"),
93
+ gray: f("\x1B[90m", "\x1B[39m"),
94
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
95
+ bgRed: f("\x1B[41m", "\x1B[49m"),
96
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
97
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
98
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
99
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
100
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
101
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
102
+ blackBright: f("\x1B[90m", "\x1B[39m"),
103
+ redBright: f("\x1B[91m", "\x1B[39m"),
104
+ greenBright: f("\x1B[92m", "\x1B[39m"),
105
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
106
+ blueBright: f("\x1B[94m", "\x1B[39m"),
107
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
108
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
109
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
110
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
111
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
112
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
113
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
114
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
115
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
116
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
117
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m"),
118
+ rgb,
119
+ bgRgb,
120
+ hex: (color) => rgb(...parseHexColor(color)),
121
+ bgHex: (color) => bgRgb(...parseHexColor(color))
122
+ };
123
+ }
124
+ //#endregion
125
+ export { createColors, createColors as default };
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@mpen/ansi-colors",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "exports": {
14
+ ".": "./dist/index.js",
15
+ "./package.json": "./package.json"
16
+ },
17
+ "types": "./dist/index.d.ts",
18
+ "devDependencies": {
19
+ "@types/bun": "latest",
20
+ "tsdown": "^0.21",
21
+ "typescript": "^6"
22
+ },
23
+ "scripts": {
24
+ "build": "bun run --bun tsdown"
25
+ }
26
+ }