|
| 1 | +# yanse |
| 2 | + |
| 3 | +<p align="center"> |
| 4 | + <img src="https://raw.githubusercontent.com/hyperweb-io/dev-utils/refs/heads/main/docs/img/logo.svg" width="80"> |
| 5 | + <br /> |
| 6 | + Yanse (颜色) - Fast terminal color styling |
| 7 | + <br /> |
| 8 | + <a href="https://github.com/hyperweb-io/dev-utils/actions/workflows/ci.yml"> |
| 9 | + <img height="20" src="https://github.com/hyperweb-io/dev-utils/actions/workflows/ci.yml/badge.svg" /> |
| 10 | + </a> |
| 11 | + <a href="https://github.com/hyperweb-io/dev-utils/blob/main/LICENSE"> |
| 12 | + <img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/> |
| 13 | + </a> |
| 14 | +</p> |
| 15 | + |
| 16 | +Fast and lightweight terminal color styling library with a chalk-like API. Yanse (颜色, yánsè) means "color" in Chinese. |
| 17 | + |
| 18 | +Why? We got tired of chalk's ESM-only errors and needed control over our dependencies. This utility is too simple to justify depending on chalk and wrestling with `module: true`. |
| 19 | + |
| 20 | +## Features |
| 21 | + |
| 22 | +- **Fast & Lightweight** - Zero dependencies, optimized for performance |
| 23 | +- **Chalk-like API** - Drop-in replacement for chalk with familiar syntax |
| 24 | +- **TypeScript Support** - Fully typed with comprehensive type definitions |
| 25 | +- **Nested Colors** - Proper handling of nested color styles without bugs |
| 26 | +- **Chained Styles** - Chain multiple colors and modifiers |
| 27 | +- **Toggle Support** - Easily enable/disable colors |
| 28 | +- **Themes & Aliases** - Create custom color themes and aliases |
| 29 | + |
| 30 | +## Install |
| 31 | + |
| 32 | +```sh |
| 33 | +npm install yanse |
| 34 | +``` |
| 35 | + |
| 36 | +## Usage |
| 37 | + |
| 38 | +### Basic Colors |
| 39 | + |
| 40 | +```typescript |
| 41 | +import yanse, { red, green, blue, yellow, cyan } from 'yanse'; |
| 42 | + |
| 43 | +console.log(red('Error message')); |
| 44 | +console.log(green('Success message')); |
| 45 | +console.log(blue('Info message')); |
| 46 | +console.log(yellow('Warning message')); |
| 47 | +console.log(cyan('Debug message')); |
| 48 | +``` |
| 49 | + |
| 50 | +### Chained Colors |
| 51 | + |
| 52 | +```typescript |
| 53 | +import yanse from 'yanse'; |
| 54 | + |
| 55 | +console.log(yanse.bold.red('Bold red text')); |
| 56 | +console.log(yanse.bold.yellow.italic('Bold yellow italic text')); |
| 57 | +console.log(yanse.green.bold.underline('Bold green underlined text')); |
| 58 | +``` |
| 59 | + |
| 60 | +### Nested Colors |
| 61 | + |
| 62 | +```typescript |
| 63 | +import { yellow, red, cyan } from 'yanse'; |
| 64 | + |
| 65 | +console.log(yellow(`foo ${red.bold('red')} bar ${cyan('cyan')} baz`)); |
| 66 | +``` |
| 67 | + |
| 68 | +### Logger Example |
| 69 | + |
| 70 | +Perfect for building loggers with colored output: |
| 71 | + |
| 72 | +```typescript |
| 73 | +import yanse, { cyan, yellow, red, green, bold } from 'yanse'; |
| 74 | + |
| 75 | +type LogLevel = 'info' | 'warn' | 'error' | 'debug' | 'success'; |
| 76 | + |
| 77 | +const levelColors: Record<LogLevel, typeof cyan> = { |
| 78 | + info: cyan, |
| 79 | + warn: yellow, |
| 80 | + error: red, |
| 81 | + debug: yanse.gray, |
| 82 | + success: green |
| 83 | +}; |
| 84 | + |
| 85 | +class Logger { |
| 86 | + constructor(private scope: string) {} |
| 87 | + |
| 88 | + log(level: LogLevel, message: string) { |
| 89 | + const tag = bold(`[${this.scope}]`); |
| 90 | + const color = levelColors[level]; |
| 91 | + const prefix = color(`${level.toUpperCase()}:`); |
| 92 | + |
| 93 | + console.log(`${tag} ${prefix} ${message}`); |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +const logger = new Logger('MyApp'); |
| 98 | +logger.log('info', 'Application started'); |
| 99 | +logger.log('success', 'Connection established'); |
| 100 | +logger.log('warn', 'Deprecated API used'); |
| 101 | +logger.log('error', 'Failed to connect'); |
| 102 | +``` |
| 103 | + |
| 104 | +## Available Styles |
| 105 | + |
| 106 | +### Colors |
| 107 | + |
| 108 | +- `black` |
| 109 | +- `red` |
| 110 | +- `green` |
| 111 | +- `yellow` |
| 112 | +- `blue` |
| 113 | +- `magenta` |
| 114 | +- `cyan` |
| 115 | +- `white` |
| 116 | +- `gray` / `grey` |
| 117 | + |
| 118 | +### Background Colors |
| 119 | + |
| 120 | +- `bgBlack` |
| 121 | +- `bgRed` |
| 122 | +- `bgGreen` |
| 123 | +- `bgYellow` |
| 124 | +- `bgBlue` |
| 125 | +- `bgMagenta` |
| 126 | +- `bgCyan` |
| 127 | +- `bgWhite` |
| 128 | + |
| 129 | +### Bright Colors |
| 130 | + |
| 131 | +- `blackBright`, `redBright`, `greenBright`, `yellowBright` |
| 132 | +- `blueBright`, `magentaBright`, `cyanBright`, `whiteBright` |
| 133 | + |
| 134 | +### Bright Background Colors |
| 135 | + |
| 136 | +- `bgBlackBright`, `bgRedBright`, `bgGreenBright`, `bgYellowBright` |
| 137 | +- `bgBlueBright`, `bgMagentaBright`, `bgCyanBright`, `bgWhiteBright` |
| 138 | + |
| 139 | +### Style Modifiers |
| 140 | + |
| 141 | +- `bold` |
| 142 | +- `dim` |
| 143 | +- `italic` |
| 144 | +- `underline` |
| 145 | +- `inverse` |
| 146 | +- `hidden` |
| 147 | +- `strikethrough` |
| 148 | +- `reset` |
| 149 | + |
| 150 | +## Toggle Color Support |
| 151 | + |
| 152 | +```typescript |
| 153 | +import yanse from 'yanse'; |
| 154 | + |
| 155 | +// Disable colors |
| 156 | +yanse.enabled = false; |
| 157 | +console.log(yanse.red('This will not be colored')); |
| 158 | + |
| 159 | +// Re-enable colors |
| 160 | +yanse.enabled = true; |
| 161 | +console.log(yanse.red('This will be red')); |
| 162 | +``` |
| 163 | + |
| 164 | +## Strip ANSI Codes |
| 165 | + |
| 166 | +```typescript |
| 167 | +import yanse from 'yanse'; |
| 168 | + |
| 169 | +const styled = yanse.blue.bold('Hello World'); |
| 170 | +console.log(yanse.unstyle(styled)); // 'Hello World' |
| 171 | +console.log(yanse.stripColor(styled)); // 'Hello World' (alias) |
| 172 | +``` |
| 173 | + |
| 174 | +## Themes & Aliases |
| 175 | + |
| 176 | +### Create Aliases |
| 177 | + |
| 178 | +```typescript |
| 179 | +import yanse from 'yanse'; |
| 180 | + |
| 181 | +yanse.alias('primary', yanse.blue); |
| 182 | +yanse.alias('secondary', yanse.gray); |
| 183 | + |
| 184 | +console.log(yanse.primary('Primary text')); |
| 185 | +console.log(yanse.secondary('Secondary text')); |
| 186 | +``` |
| 187 | + |
| 188 | +### Create Themes |
| 189 | + |
| 190 | +```typescript |
| 191 | +import yanse from 'yanse'; |
| 192 | + |
| 193 | +yanse.theme({ |
| 194 | + danger: yanse.red, |
| 195 | + success: yanse.green, |
| 196 | + warning: yanse.yellow, |
| 197 | + info: yanse.cyan, |
| 198 | + primary: yanse.blue, |
| 199 | + muted: yanse.dim.gray |
| 200 | +}); |
| 201 | + |
| 202 | +console.log(yanse.danger('Error occurred!')); |
| 203 | +console.log(yanse.success('Operation successful!')); |
| 204 | +console.log(yanse.warning('Be careful!')); |
| 205 | +``` |
| 206 | + |
| 207 | +## Create Custom Instances |
| 208 | + |
| 209 | +```typescript |
| 210 | +import { create } from 'yanse'; |
| 211 | + |
| 212 | +const customYanse = create(); |
| 213 | +customYanse.enabled = false; // This instance has colors disabled |
| 214 | + |
| 215 | +console.log(customYanse.red('Not colored')); |
| 216 | +``` |
| 217 | + |
| 218 | +## API |
| 219 | + |
| 220 | +### Properties |
| 221 | + |
| 222 | +- `enabled: boolean` - Enable/disable color output |
| 223 | +- `visible: boolean` - Make output visible/invisible |
| 224 | +- `ansiRegex: RegExp` - Regex for matching ANSI codes |
| 225 | + |
| 226 | +### Methods |
| 227 | + |
| 228 | +- `hasColor(str: string): boolean` - Check if string contains ANSI codes |
| 229 | +- `hasAnsi(str: string): boolean` - Alias for hasColor |
| 230 | +- `unstyle(str: string): string` - Remove ANSI codes from string |
| 231 | +- `stripColor(str: string): string` - Alias for unstyle |
| 232 | +- `alias(name: string, color: YanseColor): void` - Create color alias |
| 233 | +- `theme(colors: Record<string, YanseColor>): void` - Create color theme |
| 234 | +- `create(): YanseColors` - Create new yanse instance |
| 235 | + |
| 236 | +## Why Yanse? |
| 237 | + |
| 238 | +- **Zero Dependencies** - No external dependencies, minimal bundle size |
| 239 | +- **Fast** - Optimized for performance |
| 240 | +- **Correct Nested Colors** - Unlike some libraries, yanse correctly handles nested color styles |
| 241 | +- **TypeScript First** - Written in TypeScript with full type support |
| 242 | +- **Familiar API** - Drop-in replacement for chalk |
| 243 | + |
| 244 | +## Developing |
| 245 | + |
| 246 | +When first cloning the repo: |
| 247 | + |
| 248 | +```sh |
| 249 | +pnpm install |
| 250 | +pnpm build |
| 251 | +``` |
| 252 | + |
| 253 | +## Testing |
| 254 | + |
| 255 | +```sh |
| 256 | +pnpm test |
| 257 | +``` |
| 258 | + |
| 259 | +## Credits |
| 260 | + |
| 261 | +Inspired by [chalk](https://github.com/chalk/chalk) and [ansi-colors](https://github.com/doowb/ansi-colors). |
0 commit comments