printstyle 1.0.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.
Files changed (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/index.d.ts +124 -0
  4. package/index.js +190 -0
  5. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Stamat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # PrintStyle 🌈
2
+
3
+ ANSI terminal styling with a built-in micro template language.
4
+
5
+ Most color libraries make you chain function calls or concatenate escape codes by hand. PrintStyle gives you `paint()` — a template syntax that reads like markup and just works.
6
+
7
+ ```js
8
+ import PrintStyle from "printstyle";
9
+ const ps = new PrintStyle();
10
+
11
+ ps.paint("{red|Error:} Woops something went wrong!");
12
+ ```
13
+
14
+ ## Why PrintStyle?
15
+
16
+ - **Template syntax** — `paint()` lets you style strings inline without concatenation chains or nested calls. No other terminal color package has this built in.
17
+ - **Tiny** — ~9 KB unpacked, zero dependencies.
18
+ - **Hex colors** — use any color, not just the 16 built-in ANSI names.
19
+ - **NO_COLOR / FORCE_COLOR** — follows the standard out of the box.
20
+ - **No magic** — plain class with string properties. No Proxy, no prototype tricks, no chaining API. Easy to debug, easy to understand.
21
+
22
+ **Note:** Legacy Windows CMD (before Windows 10) does not support ANSI escape codes. Use Windows Terminal, PowerShell, or WSL instead.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm install printstyle
28
+ ```
29
+
30
+ ## Template Syntax — `paint()`
31
+
32
+ ### Wrap mode (auto-reset)
33
+
34
+ Style is applied and automatically reset after the content:
35
+
36
+ ```js
37
+ ps.paint("{red|error message}"); // red text, then reset
38
+ ps.paint("{bold.red|ERROR}"); // bold + red, then reset
39
+ ps.paint("{bgRed.white| FAIL }"); // red background, white text
40
+ ```
41
+
42
+ ### Spanning mode (manual reset)
43
+
44
+ Style stays active until you close with `{/}`:
45
+
46
+ ```js
47
+ ps.paint("{red}all of this is red{/}");
48
+ ps.paint("{dim}prefix {bold|inner} still dim{/}");
49
+ ```
50
+
51
+ ### Dot-chaining
52
+
53
+ Combine multiple styles with `.`:
54
+
55
+ ```js
56
+ ps.paint("{bgRed.white.bold|FAIL}"); // background + foreground + style
57
+ ps.paint("{green.bold|✓} {dim|file.jpg}"); // multiple wraps in one string
58
+ ```
59
+
60
+ ### Hex colors
61
+
62
+ Use any hex color for foreground or background:
63
+
64
+ ```js
65
+ ps.paint("{#2e8b57|sea green text}");
66
+ ps.paint("{bg#ff5500.white|orange background}");
67
+ ps.paint("{#f00|shorthand red}"); // 3-char hex supported
68
+ ```
69
+
70
+ ## Direct Property Access
71
+
72
+ Every ANSI code is also available as a string property for manual concatenation:
73
+
74
+ ```js
75
+ console.log(ps.red + "error" + ps.reset);
76
+ console.log(ps.bold + ps.blue + "title" + ps.reset);
77
+ ```
78
+
79
+ ### Available properties
80
+
81
+ **Styles:** `reset` `bold` `dim` `italic` `underline` `blink` `inverse` `hidden` `strikethrough`
82
+
83
+ **Colors:** `black` `red` `green` `yellow` `blue` `magenta` `cyan` `white` `gray`
84
+
85
+ **Bright colors:** `redBright` `greenBright` `yellowBright` `blueBright` `magentaBright` `cyanBright` `whiteBright`
86
+
87
+ **Backgrounds:** `bgBlack` `bgRed` `bgGreen` `bgYellow` `bgBlue` `bgMagenta` `bgCyan` `bgWhite` `bgGray`
88
+
89
+ **Bright backgrounds:** `bgRedBright` `bgGreenBright` `bgYellowBright` `bgBlueBright` `bgMagentaBright` `bgCyanBright` `bgWhiteBright`
90
+
91
+ **Other:** `bell`
92
+
93
+ ## Custom Styles — `extend()`
94
+
95
+ Register your own named styles built from any combination of built-in codes or hex colors:
96
+
97
+ ```js
98
+ const ps = new PrintStyle();
99
+
100
+ ps.extend({
101
+ error: ps.bold + ps.red,
102
+ success: ps.bold + ps.green,
103
+ warning: ps.yellow,
104
+ brand: ps.color("#ff6600"),
105
+ link: ps.blue + ps.underline + ps.italic,
106
+ });
107
+
108
+ console.log(ps.paint("{error|Something failed}"));
109
+ console.log(ps.paint("{success|All good}"));
110
+ console.log(ps.paint("{brand|Acme Corp}"));
111
+ console.log(ps.paint("{link|https://github.com/stamat/printstyle}"));
112
+ ```
113
+
114
+ Custom styles work everywhere built-in styles do — in `paint()` templates, dot-chaining, direct property access, and they integrate with `disable()` / `enable()`.
115
+
116
+ ## Hex Color Methods
117
+
118
+ ```js
119
+ ps.color("#ff0000"); // foreground escape sequence for hex
120
+ ps.background("#00ff00"); // background escape sequence for hex
121
+ ```
122
+
123
+ Both accept 6-char (`#ff0000`) or 3-char (`#f00`) hex, with or without `#`.
124
+
125
+ ## NO_COLOR / FORCE_COLOR
126
+
127
+ PrintStyle respects the [NO_COLOR](https://no-color.org/) and [FORCE_COLOR](https://force-color.org/) environment variables automatically:
128
+
129
+ - `NO_COLOR` (any value) — disables all color output
130
+ - `FORCE_COLOR=1` — forces color output regardless of `NO_COLOR`
131
+ - `FORCE_COLOR=0` — disables color output
132
+
133
+ You can also control it programmatically:
134
+
135
+ ```js
136
+ ps.disable(); // turn off all codes
137
+ ps.enable(); // restore all codes
138
+ ps.enabled; // check current state
139
+ ```
140
+
141
+ ## Comparison
142
+
143
+ | | printstyle | chalk | ansis | picocolors | kleur | ansi-colors | yoctocolors |
144
+ | ------------------- | ---------- | ------------ | --------- | ---------- | --------- | ----------- | ----------- |
145
+ | **Unpacked size** | ~9 KB | ~44 KB | ~6 KB | ~6 KB | ~20 KB | ~26 KB | ~10 KB |
146
+ | **Runtime deps** | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
147
+ | **Template syntax** | Built-in | Separate pkg | No | No | No | No | No |
148
+ | **Hex colors** | Yes | Yes | Yes | No | No | No | No |
149
+ | **NO_COLOR** | Yes | No | Yes | Yes | Yes | No | Yes |
150
+ | **FORCE_COLOR** | Yes | Yes | Yes | Yes | Yes | Partial | Yes |
151
+ | **TypeScript** | Built-in | Built-in | Built-in | Built-in | Built-in | Built-in | Built-in |
152
+ | **Module type** | ESM | ESM | CJS + ESM | CJS + ESM | CJS + ESM | CJS | ESM |
153
+ | **Legacy Win CMD** | No | Yes | No | No | No | No | No |
154
+
155
+ ## License
156
+
157
+ [MIT](LICENSE)
158
+
159
+ ## P.S.
160
+
161
+ > Another one - DJ Khaled
162
+
163
+ ❤️, @stamat
package/index.d.ts ADDED
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Minimal ANSI terminal styling with a built-in micro template language.
3
+ */
4
+ export default class PrintStyle {
5
+ /** Whether ANSI codes are currently active. */
6
+ enabled: boolean
7
+
8
+ // Styles
9
+ reset: string
10
+ bold: string
11
+ dim: string
12
+ italic: string
13
+ underline: string
14
+ blink: string
15
+ inverse: string
16
+ hidden: string
17
+ strikethrough: string
18
+
19
+ // Foreground colors
20
+ black: string
21
+ red: string
22
+ redBright: string
23
+ green: string
24
+ greenBright: string
25
+ yellow: string
26
+ yellowBright: string
27
+ blue: string
28
+ blueBright: string
29
+ magenta: string
30
+ magentaBright: string
31
+ cyan: string
32
+ cyanBright: string
33
+ white: string
34
+ whiteBright: string
35
+ gray: string
36
+
37
+ // Background colors
38
+ bgBlack: string
39
+ bgRed: string
40
+ bgRedBright: string
41
+ bgGreen: string
42
+ bgGreenBright: string
43
+ bgYellow: string
44
+ bgYellowBright: string
45
+ bgBlue: string
46
+ bgBlueBright: string
47
+ bgMagenta: string
48
+ bgMagentaBright: string
49
+ bgCyan: string
50
+ bgCyanBright: string
51
+ bgWhite: string
52
+ bgWhiteBright: string
53
+ bgGray: string
54
+
55
+ // Other
56
+ bell: string
57
+
58
+ /**
59
+ * Disable all ANSI codes. Properties become empty strings.
60
+ * The `bell` property is preserved.
61
+ */
62
+ disable(): void
63
+
64
+ /**
65
+ * Re-enable all ANSI codes, restoring their original values.
66
+ */
67
+ enable(): void
68
+
69
+ /**
70
+ * Register custom named styles.
71
+ * Extended styles integrate with `disable()` and `enable()` and are usable in `paint()` templates.
72
+ * @param styles An object mapping style names to ANSI escape strings.
73
+ */
74
+ extend(styles: Record<string, string>): void
75
+
76
+ /**
77
+ * Parse a hex color string into an [R, G, B] array.
78
+ * @param hex Hex color string (e.g. `'#ff0000'`, `'f00'`).
79
+ * @returns An array of [red, green, blue] values (0–255).
80
+ */
81
+ hexToRgb(hex: string): [number, number, number]
82
+
83
+ /**
84
+ * Convert RGB values to a 256-color terminal index.
85
+ * @param red Red channel (0–255).
86
+ * @param green Green channel (0–255).
87
+ * @param blue Blue channel (0–255).
88
+ * @returns The terminal color index (16–231).
89
+ */
90
+ terminalColorIndex(red: number, green: number, blue: number): number
91
+
92
+ /**
93
+ * Get a 256-color foreground escape sequence for a hex color.
94
+ * @param hex Hex color string (e.g. `'#ff0000'`, `'f00'`).
95
+ * @returns The ANSI escape sequence, or `''` if disabled.
96
+ */
97
+ color(hex: string): string
98
+
99
+ /**
100
+ * Get a 256-color background escape sequence for a hex color.
101
+ * @param hex Hex color string (e.g. `'#ff0000'`, `'f00'`).
102
+ * @returns The ANSI escape sequence, or `''` if disabled.
103
+ */
104
+ background(hex: string): string
105
+
106
+ /**
107
+ * Apply styles to a string using a micro template syntax.
108
+ *
109
+ * **Wrap mode** (auto-reset): `{style|content}` — e.g. `'{red|error}'`
110
+ *
111
+ * **Spanning mode** (manual reset): `{style}...{/}` — e.g. `'{red}error{/}'`
112
+ *
113
+ * Styles can be dot-chained: `'{bold.red|ERROR}'`
114
+ *
115
+ * Hex colors: `'{#2e8b57|text}'`, `'{bg#ff5500|text}'`
116
+ *
117
+ * @param str The template string to process.
118
+ * @returns The string with ANSI escape sequences applied.
119
+ */
120
+ paint(str: string): string
121
+
122
+ /** Allow custom styles added via `extend()`. */
123
+ [key: string]: string | boolean | ((...args: any[]) => any)
124
+ }
package/index.js ADDED
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Minimal ANSI terminal styling with a built-in micro template language.
3
+ */
4
+ export default class PrintStyle {
5
+ enabled = true
6
+ reset = '\x1b[0m'
7
+ bold = '\x1b[1m'
8
+ dim = '\x1b[2m'
9
+ italic = '\x1b[3m'
10
+ underline = '\x1b[4m'
11
+ blink = '\x1b[5m'
12
+ inverse = '\x1b[7m'
13
+ hidden = '\x1b[8m'
14
+ strikethrough = '\x1b[9m'
15
+ black = '\x1b[30m'
16
+ red = '\x1b[31m'
17
+ redBright = '\x1b[91m'
18
+ green = '\x1b[32m'
19
+ greenBright = '\x1b[92m'
20
+ yellow = '\x1b[33m'
21
+ yellowBright = '\x1b[93m'
22
+ blue = '\x1b[34m'
23
+ blueBright = '\x1b[94m'
24
+ magenta = '\x1b[35m'
25
+ magentaBright = '\x1b[95m'
26
+ cyan = '\x1b[36m'
27
+ cyanBright = '\x1b[96m'
28
+ white = '\x1b[37m'
29
+ whiteBright = '\x1b[97m'
30
+ gray = '\x1b[90m'
31
+ bgBlack = '\x1b[40m'
32
+ bgRed = '\x1b[41m'
33
+ bgRedBright = '\x1b[101m'
34
+ bgGreen = '\x1b[42m'
35
+ bgGreenBright = '\x1b[102m'
36
+ bgYellow = '\x1b[43m'
37
+ bgYellowBright = '\x1b[103m'
38
+ bgBlue = '\x1b[44m'
39
+ bgBlueBright = '\x1b[104m'
40
+ bgMagenta = '\x1b[45m'
41
+ bgMagentaBright = '\x1b[105m'
42
+ bgCyan = '\x1b[46m'
43
+ bgCyanBright = '\x1b[106m'
44
+ bgWhite = '\x1b[47m'
45
+ bgWhiteBright = '\x1b[107m'
46
+ bgGray = '\x1b[100m'
47
+ bell = '\x07'
48
+
49
+ constructor() {
50
+ this._defaults = {}
51
+ for (const key of Object.keys(this)) {
52
+ if (typeof this[key] === 'string') this._defaults[key] = this[key]
53
+ }
54
+
55
+ const env = typeof process !== 'undefined' && process.env ? process.env : {}
56
+ if ('FORCE_COLOR' in env) {
57
+ if (env.FORCE_COLOR === '0') this.disable()
58
+ } else if ('NO_COLOR' in env) {
59
+ this.disable()
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Disable all ANSI codes. Properties become empty strings.
65
+ * The `bell` property is preserved.
66
+ */
67
+ disable() {
68
+ this.enabled = false
69
+ for (const key of Object.keys(this._defaults)) {
70
+ if (key === 'bell') continue
71
+ this[key] = ''
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Re-enable all ANSI codes, restoring their original values.
77
+ */
78
+ enable() {
79
+ this.enabled = true
80
+ for (const [key, value] of Object.entries(this._defaults)) {
81
+ this[key] = value
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Register custom named styles.
87
+ * Extended styles integrate with `disable()` and `enable()` and are usable in `paint()` templates.
88
+ * @param {Record<string, string>} styles - An object mapping style names to ANSI escape strings.
89
+ * @example
90
+ * ps.extend({
91
+ * error: ps.bold + ps.red,
92
+ * brand: ps.color('#ff6600'),
93
+ * })
94
+ * ps.paint('{error|Something failed}')
95
+ */
96
+ extend(styles) {
97
+ for (const [key, value] of Object.entries(styles)) {
98
+ this[key] = value
99
+ this._defaults[key] = value
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Parse a hex color string into an [R, G, B] array.
105
+ * @param {string} hex - Hex color string (e.g. `'#ff0000'`, `'f00'`).
106
+ * @returns {number[]} An array of [red, green, blue] values (0–255).
107
+ */
108
+ hexToRgb(hex) {
109
+ let h = hex.replace('#', '')
110
+ if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2]
111
+ const red = parseInt(h.substring(0, 2), 16)
112
+ const green = parseInt(h.substring(2, 4), 16)
113
+ const blue = parseInt(h.substring(4, 6), 16)
114
+
115
+ return [
116
+ isNaN(red) ? 0 : red,
117
+ isNaN(green) ? 0 : green,
118
+ isNaN(blue) ? 0 : blue
119
+ ]
120
+ }
121
+
122
+ /**
123
+ * Convert RGB values to a 256-color terminal index.
124
+ * @param {number} red - Red channel (0–255).
125
+ * @param {number} green - Green channel (0–255).
126
+ * @param {number} blue - Blue channel (0–255).
127
+ * @returns {number} The terminal color index (16–231).
128
+ */
129
+ terminalColorIndex(red, green, blue) {
130
+ return 16 +
131
+ Math.round(red / 255 * 5) * 36 +
132
+ Math.round(green / 255 * 5) * 6 +
133
+ Math.round(blue / 255 * 5)
134
+ }
135
+
136
+ /**
137
+ * Get a 256-color foreground escape sequence for a hex color.
138
+ * @param {string} hex - Hex color string (e.g. `'#ff0000'`, `'f00'`).
139
+ * @returns {string} The ANSI escape sequence, or `''` if disabled.
140
+ */
141
+ color(hex) {
142
+ if (!this.enabled) return ''
143
+ const [red, green, blue] = this.hexToRgb(hex)
144
+
145
+ return `\x1b[38;5;${this.terminalColorIndex(red, green, blue)}m`
146
+ }
147
+
148
+ /**
149
+ * Get a 256-color background escape sequence for a hex color.
150
+ * @param {string} hex - Hex color string (e.g. `'#ff0000'`, `'f00'`).
151
+ * @returns {string} The ANSI escape sequence, or `''` if disabled.
152
+ */
153
+ background(hex) {
154
+ if (!this.enabled) return ''
155
+ const [red, green, blue] = this.hexToRgb(hex)
156
+
157
+ return `\x1b[48;5;${this.terminalColorIndex(red, green, blue)}m`
158
+ }
159
+
160
+ /** @private */
161
+ _resolveToken(token) {
162
+ if (token === '/') return this.reset
163
+ if (token.startsWith('bg#')) return this.background(token.slice(2))
164
+ if (token.startsWith('#')) return this.color(token)
165
+ return (typeof this[token] === 'string') ? this[token] : ''
166
+ }
167
+
168
+ /**
169
+ * Apply styles to a string using a micro template syntax.
170
+ *
171
+ * **Wrap mode** (auto-reset): `{style|content}` — e.g. `'{red|error}'`
172
+ *
173
+ * **Spanning mode** (manual reset): `{style}...{/}` — e.g. `'{red}error{/}'`
174
+ *
175
+ * Styles can be dot-chained: `'{bold.red|ERROR}'`
176
+ *
177
+ * Hex colors: `'{#2e8b57|text}'`, `'{bg#ff5500|text}'`
178
+ *
179
+ * @param {string} str - The template string to process.
180
+ * @returns {string} The string with ANSI escape sequences applied.
181
+ */
182
+ paint(str) {
183
+ return str.replace(/\{([^}]+?)(?:\|([^}]*))?\}/g, (_, style, content) => {
184
+ if (style === '/') return this.reset
185
+ const codes = style.split('.').map(t => this._resolveToken(t)).join('')
186
+ if (content !== undefined) return codes + content + this.reset
187
+ return codes
188
+ })
189
+ }
190
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "printstyle",
3
+ "description": "Minimal, zero-dependency ANSI terminal styling with a micro template language.",
4
+ "version": "1.0.0",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "index.js",
8
+ "types": "index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "default": "./index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "index.d.ts"
18
+ ],
19
+ "author": "Stamat <@stamat> (http://stamat.info)",
20
+ "homepage": "https://github.com/stamat/printstyle",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/stamat/printstyle.git"
24
+ },
25
+ "private": false,
26
+ "keywords": [
27
+ "ansi",
28
+ "colors",
29
+ "style",
30
+ "template",
31
+ "cli",
32
+ "zero-dependency"
33
+ ],
34
+ "scripts": {
35
+ "lint": "eslint ./index.js",
36
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest"
37
+ },
38
+ "devDependencies": {
39
+ "@jest/globals": "^30.2.0",
40
+ "eslint": "^9.39.3",
41
+ "jest": "^30.2.0",
42
+ "neostandard": "^0.12.2"
43
+ }
44
+ }