@reliverse/relico 1.0.0 → 1.0.2

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 CHANGED
@@ -11,7 +11,7 @@
11
11
  </a>
12
12
  </p>
13
13
 
14
- **@reliverse/relico is a modern terminal color formatting library** that provides a type-safe and flexible way to add colors and styles to your command-line output. Built with TypeScript and TypeBox for enhanced reliability and developer experience.
14
+ **@reliverse/relico is an accessible and modern terminal color formatting library** that provides a type-safe and flexible way to add colors and styles to your command-line output. Built with TypeScript and TypeBox for enhanced reliability and developer experience.
15
15
 
16
16
  ## Installation
17
17
 
@@ -19,6 +19,48 @@
19
19
  bun add @reliverse/relico # Replace "bun" with npm, pnpm, or yarn if desired
20
20
  ```
21
21
 
22
+ **Configure `relico.config.ts`**:
23
+
24
+ ```ts
25
+ import { defineConfig } from "@reliverse/relico";
26
+
27
+ export default defineConfig({
28
+ // Set the color level: 3 for truecolor
29
+ colorLevel: 3,
30
+ // Choose the theme: "primary" or "secondary"
31
+ theme: "secondary",
32
+ // Override specific colors
33
+ // Use Intellisense to see the available colors
34
+ customColors: {
35
+ blue: ["#5f87ff", "#5f87ff"],
36
+ red: ["#ff5555", "#ff0000"],
37
+ green: ["#00ff00", "#00cc00"],
38
+ // Note: The following text formatting
39
+ // colors can be defined only via ANSI:
40
+ // reset: ["\x1b[0m", "\x1b[0m"],
41
+ // bold: ["\x1b[1m", "\x1b[22m", "\x1b[22m\x1b[1m"],
42
+ // dim: ["\x1b[2m", "\x1b[22m", "\x1b[22m\x1b[2m"],
43
+ // italic: ["\x1b[3m", "\x1b[23m"],
44
+ // underline: ["\x1b[4m", "\x1b[24m"],
45
+ // inverse: ["\x1b[7m", "\x1b[27m"],
46
+ // hidden: ["\x1b[8m", "\x1b[28m"],
47
+ // strikethrough: ["\x1b[9m", "\x1b[29m"],
48
+ },
49
+ });
50
+ ```
51
+
52
+ **To load config (example)**:
53
+
54
+ ```ts
55
+ // Initialize user config, so you override any relico's default settings
56
+ function main() {
57
+ console.log(re.blue("Loading user config..."));
58
+ await initUserConfig();
59
+ console.log(re.blue("If this blue log looks different than line above, then config is loaded successfully. Yay! 🎉"));
60
+ }
61
+ main();
62
+ ```
63
+
22
64
  ## Key Features
23
65
 
24
66
  - **Smart Terminal Detection**: Automatically detects terminal type and color support level
package/bin/main.d.ts ADDED
@@ -0,0 +1,130 @@
1
+ /** A color definition: [primary, secondary, optional replacement]. */
2
+ export type ColorDefinition = [string, string, string?];
3
+ /** A list of default color keys. */
4
+ export declare const defaultColorKeys: readonly ["reset", "bold", "dim", "italic", "underline", "inverse", "hidden", "strikethrough", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray", "bgBlack", "bgRed", "bgGreen", "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite", "blackBright", "redBright", "greenBright", "yellowBright", "blueBright", "magentaBright", "cyanBright", "whiteBright", "bgBlackBright", "bgRedBright", "bgGreenBright", "bgYellowBright", "bgBlueBright", "bgMagentaBright", "bgCyanBright", "bgWhiteBright"];
5
+ export type DefaultColorKeys = (typeof defaultColorKeys)[number];
6
+ /** A map of custom color definitions. IntelliSense will suggest only valid keys. */
7
+ export type ColorMap = Partial<Record<DefaultColorKeys, ColorDefinition>>;
8
+ /**
9
+ * `relico.config.ts` configuration options.
10
+ */
11
+ export type RelicoConfig = {
12
+ /**
13
+ * Determines which ANSI mode is used:
14
+ * - 0: no color
15
+ * - 1: basic ANSI (8 colors)
16
+ * - 2: 256 color palette
17
+ * - 3: 24-bit truecolor
18
+ */
19
+ colorLevel?: 0 | 1 | 2 | 3;
20
+ /**
21
+ * Theme to use for color definitions.
22
+ * - "primary": primary theme
23
+ * - "secondary": secondary theme
24
+ */
25
+ theme?: "primary" | "secondary";
26
+ /**
27
+ * Custom color definitions.
28
+ * - Use IntelliSense to see available colors.
29
+ * - Theming: ["primary", "secondary"]
30
+ */
31
+ customColors?: ColorMap;
32
+ };
33
+ export type IRelicoColors = {
34
+ reset(text: string | number): string;
35
+ bold(text: string | number): string;
36
+ dim(text: string | number): string;
37
+ italic(text: string | number): string;
38
+ underline(text: string | number): string;
39
+ inverse(text: string | number): string;
40
+ hidden(text: string | number): string;
41
+ strikethrough(text: string | number): string;
42
+ black(text: string | number): string;
43
+ red(text: string | number): string;
44
+ green(text: string | number): string;
45
+ yellow(text: string | number): string;
46
+ blue(text: string | number): string;
47
+ magenta(text: string | number): string;
48
+ cyan(text: string | number): string;
49
+ white(text: string | number): string;
50
+ gray(text: string | number): string;
51
+ bgBlack(text: string | number): string;
52
+ bgRed(text: string | number): string;
53
+ bgGreen(text: string | number): string;
54
+ bgYellow(text: string | number): string;
55
+ bgBlue(text: string | number): string;
56
+ bgMagenta(text: string | number): string;
57
+ bgCyan(text: string | number): string;
58
+ bgWhite(text: string | number): string;
59
+ blackBright(text: string | number): string;
60
+ redBright(text: string | number): string;
61
+ greenBright(text: string | number): string;
62
+ yellowBright(text: string | number): string;
63
+ blueBright(text: string | number): string;
64
+ magentaBright(text: string | number): string;
65
+ cyanBright(text: string | number): string;
66
+ whiteBright(text: string | number): string;
67
+ bgBlackBright(text: string | number): string;
68
+ bgRedBright(text: string | number): string;
69
+ bgGreenBright(text: string | number): string;
70
+ bgYellowBright(text: string | number): string;
71
+ bgBlueBright(text: string | number): string;
72
+ bgMagentaBright(text: string | number): string;
73
+ bgCyanBright(text: string | number): string;
74
+ bgWhiteBright(text: string | number): string;
75
+ [k: string]: (text: string | number) => string;
76
+ };
77
+ export declare const re: IRelicoColors;
78
+ /**
79
+ * Configures the library with a partial or complete
80
+ * `RelicoConfig`. Invalid fields are just ignored.
81
+ */
82
+ export declare function configure(userInput: unknown): void;
83
+ /** Returns a color function by name (or `reset` or identity if not found). */
84
+ export declare function getColor(name: string): (text: string | number) => string;
85
+ /** Colorizes text with a color function. */
86
+ export declare function colorize(name: string, text: string | number): string;
87
+ /** Sets the color level (0=none, 1=basic, 2=256, 3=truecolor). */
88
+ export declare function setColorLevel(level: 0 | 1 | 2 | 3): void;
89
+ /** Returns a custom "rgb" color function if level is truecolor, otherwise identity. */
90
+ export declare function rgb(r: number, g: number, b: number): (text: string | number) => string;
91
+ export type ColorSupport = {
92
+ isColorSupported: boolean;
93
+ isForced: boolean;
94
+ isDisabled: boolean;
95
+ terminalName: string;
96
+ };
97
+ export declare const colorSupport: ColorSupport;
98
+ export declare function initUserConfig(): Promise<void>;
99
+ /**
100
+ * Provides type safety and IntelliSense for user configuration.
101
+ * Example usage in `relico.config.ts`:
102
+ * ```ts
103
+ * import { defineConfig } from "@reliverse/relico-cfg";
104
+ * export default defineConfig({
105
+ * // Set the color level: 3 for truecolor
106
+ * colorLevel: 3,
107
+ * // Choose the theme: "primary" or "secondary"
108
+ * theme: "secondary",
109
+ * // Override specific colors
110
+ * // - Use Intellisense to see the available colors
111
+ * // - Theming: ["primary", "secondary"]
112
+ * customColors: {
113
+ * blue: ["#5f87ff", "#5f87ff"],
114
+ * red: ["#ff5555", "#ff0000"],
115
+ * green: ["#00ff00", "#00cc00"],
116
+ * // Note: The following text formatting
117
+ * // colors can be defined only via ANSI:
118
+ * // reset: ["\x1b[0m", "\x1b[0m"],
119
+ * // bold: ["\x1b[1m", "\x1b[22m", "\x1b[22m\x1b[1m"],
120
+ * // dim: ["\x1b[2m", "\x1b[22m", "\x1b[22m\x1b[2m"],
121
+ * // italic: ["\x1b[3m", "\x1b[23m"],
122
+ * // underline: ["\x1b[4m", "\x1b[24m"],
123
+ * // inverse: ["\x1b[7m", "\x1b[27m"],
124
+ * // hidden: ["\x1b[8m", "\x1b[28m"],
125
+ * // strikethrough: ["\x1b[9m", "\x1b[29m"],
126
+ * },
127
+ * });
128
+ * ```
129
+ */
130
+ export declare function defineConfig(config: RelicoConfig): RelicoConfig;
package/bin/main.js ADDED
@@ -0,0 +1,361 @@
1
+ import { getCurrentTerminalName } from "@reliverse/runtime";
2
+ import { env, isWindows } from "@reliverse/runtime";
3
+ import { loadConfig } from "c12";
4
+ export const defaultColorKeys = [
5
+ "reset",
6
+ "bold",
7
+ "dim",
8
+ "italic",
9
+ "underline",
10
+ "inverse",
11
+ "hidden",
12
+ "strikethrough",
13
+ "black",
14
+ "red",
15
+ "green",
16
+ "yellow",
17
+ "blue",
18
+ "magenta",
19
+ "cyan",
20
+ "white",
21
+ "gray",
22
+ "bgBlack",
23
+ "bgRed",
24
+ "bgGreen",
25
+ "bgYellow",
26
+ "bgBlue",
27
+ "bgMagenta",
28
+ "bgCyan",
29
+ "bgWhite",
30
+ "blackBright",
31
+ "redBright",
32
+ "greenBright",
33
+ "yellowBright",
34
+ "blueBright",
35
+ "magentaBright",
36
+ "cyanBright",
37
+ "whiteBright",
38
+ "bgBlackBright",
39
+ "bgRedBright",
40
+ "bgGreenBright",
41
+ "bgYellowBright",
42
+ "bgBlueBright",
43
+ "bgMagentaBright",
44
+ "bgCyanBright",
45
+ "bgWhiteBright"
46
+ ];
47
+ const argv = typeof process === "undefined" ? [] : process.argv;
48
+ const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
49
+ const isForced = "FORCE_COLOR" in env || argv.includes("--color");
50
+ const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
51
+ const isCompatibleTerminal = typeof process !== "undefined" && Boolean(process.stdout) && Boolean(process.stdout.isTTY) && env.TERM !== "dumb";
52
+ const colorterm = (env.COLORTERM ?? "").toLowerCase();
53
+ const supportsTrueColor = colorterm === "truecolor" || colorterm === "24bit";
54
+ function detectColorLevel() {
55
+ if (isDisabled) return 0;
56
+ if (isForced) return 3;
57
+ if (supportsTrueColor) return 3;
58
+ if (isWindows) return 2;
59
+ if (isCI) return 2;
60
+ if (isCompatibleTerminal) return 2;
61
+ return 0;
62
+ }
63
+ const baseColors = {
64
+ // Text formatting
65
+ reset: ["\x1B[0m", "\x1B[0m"],
66
+ bold: ["\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"],
67
+ dim: ["\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"],
68
+ italic: ["\x1B[3m", "\x1B[23m"],
69
+ underline: ["\x1B[4m", "\x1B[24m"],
70
+ inverse: ["\x1B[7m", "\x1B[27m"],
71
+ hidden: ["\x1B[8m", "\x1B[28m"],
72
+ strikethrough: ["\x1B[9m", "\x1B[29m"],
73
+ // Foreground colors.
74
+ black: ["#000000", "#000000"],
75
+ red: ["#ff5555", "#ff0000"],
76
+ green: ["#00ff00", "#00ff00"],
77
+ yellow: ["#ffff00", "#ffff00"],
78
+ blue: ["#0000ff", "#0000ff"],
79
+ magenta: ["#ff00ff", "#ff00ff"],
80
+ cyan: ["#00ffff", "#00ffff"],
81
+ white: ["#ffffff", "#ffffff"],
82
+ gray: ["#808080", "#808080"],
83
+ // Background colors.
84
+ bgBlack: ["#000000", "#000000"],
85
+ bgRed: ["#ff5555", "#ff0000"],
86
+ bgGreen: ["#00ff00", "#00ff00"],
87
+ bgYellow: ["#ffff00", "#ffff00"],
88
+ bgBlue: ["#0000ff", "#0000ff"],
89
+ bgMagenta: ["#ff00ff", "#ff00ff"],
90
+ bgCyan: ["#00ffff", "#00ffff"],
91
+ bgWhite: ["#ffffff", "#ffffff"],
92
+ // Bright colors.
93
+ blackBright: ["#000000", "#000000"],
94
+ redBright: ["#ff5555", "#ff5555"],
95
+ greenBright: ["#50fa7b", "#50fa7b"],
96
+ yellowBright: ["#f1fa8c", "#f1fa8c"],
97
+ blueBright: ["#24bdff", "#24bdff"],
98
+ magentaBright: ["#ff79c6", "#ff79c6"],
99
+ cyanBright: ["#8be9fd", "#8be9fd"],
100
+ whiteBright: ["#ffffff", "#ffffff"],
101
+ // Bright background colors.
102
+ bgBlackBright: ["#000000", "#000000"],
103
+ bgRedBright: ["#ff5555", "#ff5555"],
104
+ bgGreenBright: ["#50fa7b", "#50fa7b"],
105
+ bgYellowBright: ["#f1fa8c", "#f1fa8c"],
106
+ bgBlueBright: ["#24bdff", "#24bdff"],
107
+ bgMagentaBright: ["#ff79c6", "#ff79c6"],
108
+ bgCyanBright: ["#8be9fd", "#8be9fd"],
109
+ bgWhiteBright: ["#ffffff", "#ffffff"]
110
+ };
111
+ const windowsTerminalColors = {
112
+ ...baseColors,
113
+ red: ["#ff5555", "#ff5555"],
114
+ green: ["#50fa7b", "#50fa7b"],
115
+ yellow: ["#f1fa8c", "#f1fa8c"],
116
+ blue: ["#6272a4", "#6272a4"],
117
+ magenta: ["#ff79c6", "#ff79c6"],
118
+ cyan: ["#8be9fd", "#8be9fd"]
119
+ };
120
+ let config = {
121
+ colorLevel: detectColorLevel(),
122
+ theme: "primary"
123
+ // default theme; can be overridden in user config
124
+ };
125
+ let colorMap = {};
126
+ let colorFunctions = {};
127
+ function hexToRGB(hex) {
128
+ if (hex.startsWith("#")) {
129
+ hex = hex.slice(1);
130
+ }
131
+ if (hex.length === 3) {
132
+ hex = hex.split("").map((c) => c + c).join("");
133
+ }
134
+ if (hex.length !== 6) {
135
+ throw new Error(`Invalid hex color: ${hex}`);
136
+ }
137
+ const r = Number.parseInt(hex.substring(0, 2), 16);
138
+ const g = Number.parseInt(hex.substring(2, 4), 16);
139
+ const b = Number.parseInt(hex.substring(4, 6), 16);
140
+ return { r, g, b };
141
+ }
142
+ function hexToAnsiParts(hex, isBg = false) {
143
+ const { r, g, b } = hexToRGB(hex);
144
+ const open = isBg ? `\x1B[48;2;${r};${g};${b}m` : `\x1B[38;2;${r};${g};${b}m`;
145
+ const close = isBg ? "\x1B[49m" : "\x1B[39m";
146
+ return { open, close };
147
+ }
148
+ function hexToAnsi256(hex, isBg = false) {
149
+ const { r, g, b } = hexToRGB(hex);
150
+ const r5 = Math.round(r / 51);
151
+ const g5 = Math.round(g / 51);
152
+ const b5 = Math.round(b / 51);
153
+ const index = 16 + 36 * r5 + 6 * g5 + b5;
154
+ return isBg ? `\x1B[48;5;${index}m` : `\x1B[38;5;${index}m`;
155
+ }
156
+ const basicColors = [
157
+ { name: "black", rgb: { r: 0, g: 0, b: 0 }, fg: 30, bg: 40 },
158
+ { name: "red", rgb: { r: 205, g: 0, b: 0 }, fg: 31, bg: 41 },
159
+ { name: "green", rgb: { r: 0, g: 205, b: 0 }, fg: 32, bg: 42 },
160
+ { name: "yellow", rgb: { r: 205, g: 205, b: 0 }, fg: 33, bg: 43 },
161
+ { name: "blue", rgb: { r: 0, g: 0, b: 238 }, fg: 34, bg: 44 },
162
+ { name: "magenta", rgb: { r: 205, g: 0, b: 205 }, fg: 35, bg: 45 },
163
+ { name: "cyan", rgb: { r: 0, g: 205, b: 205 }, fg: 36, bg: 46 },
164
+ { name: "white", rgb: { r: 229, g: 229, b: 229 }, fg: 37, bg: 47 }
165
+ ];
166
+ function hexToAnsiBasic(hex, isBg = false) {
167
+ const { r, g, b } = hexToRGB(hex);
168
+ let bestMatch = basicColors[0];
169
+ let bestDistance = Number.MAX_VALUE;
170
+ for (const color of basicColors) {
171
+ const dr = r - color.rgb.r;
172
+ const dg = g - color.rgb.g;
173
+ const db = b - color.rgb.b;
174
+ const distance = dr * dr + dg * dg + db * db;
175
+ if (distance < bestDistance) {
176
+ bestDistance = distance;
177
+ bestMatch = color;
178
+ }
179
+ }
180
+ const code = isBg ? bestMatch.bg : bestMatch.fg;
181
+ return `\x1B[${code}m`;
182
+ }
183
+ function convertColorDefinition(key, def) {
184
+ const isBg = key.toLowerCase().startsWith("bg");
185
+ const theme = config.theme ?? "primary";
186
+ const chosen = theme === "primary" ? def[0] : def[1];
187
+ function convert(str) {
188
+ if (!str.startsWith("#")) return str;
189
+ if (config.colorLevel === 3) {
190
+ return hexToAnsiParts(str, isBg).open;
191
+ }
192
+ if (config.colorLevel === 2) {
193
+ return hexToAnsi256(str, isBg);
194
+ }
195
+ if (config.colorLevel === 1) {
196
+ return hexToAnsiBasic(str, isBg);
197
+ }
198
+ return "";
199
+ }
200
+ const openConverted = convert(chosen);
201
+ const close = isBg ? "\x1B[49m" : "\x1B[39m";
202
+ const rep = def[2] ? def[2].startsWith("#") ? convert(def[2]) : def[2] : void 0;
203
+ return [openConverted, close, rep];
204
+ }
205
+ function buildColorMap(cfg) {
206
+ const terminalName = getCurrentTerminalName();
207
+ const isWinTerm = terminalName === "Windows Terminal";
208
+ if (cfg.colorLevel === 0) {
209
+ const noColorMap = {};
210
+ for (const k of Object.keys(baseColors)) {
211
+ noColorMap[k] = ["", "", ""];
212
+ }
213
+ return noColorMap;
214
+ }
215
+ let builtIn = { ...baseColors };
216
+ if (cfg.customColors) {
217
+ for (const [k, v] of Object.entries(cfg.customColors)) {
218
+ builtIn[k] = v;
219
+ }
220
+ }
221
+ if (isWinTerm && cfg.colorLevel === 3) {
222
+ builtIn = { ...windowsTerminalColors, ...builtIn };
223
+ }
224
+ for (const key of Object.keys(builtIn)) {
225
+ builtIn[key] = convertColorDefinition(key, builtIn[key]);
226
+ }
227
+ return builtIn;
228
+ }
229
+ function createFormatter(open, close, replace = open) {
230
+ const escapedClose = close.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
231
+ const regex = new RegExp(escapedClose, "g");
232
+ return (input) => {
233
+ const stringed = String(input);
234
+ return open + stringed.replace(regex, replace) + close;
235
+ };
236
+ }
237
+ function identityColor(text) {
238
+ return String(text);
239
+ }
240
+ const typedRe = {
241
+ reset: identityColor,
242
+ bold: identityColor,
243
+ dim: identityColor,
244
+ italic: identityColor,
245
+ underline: identityColor,
246
+ inverse: identityColor,
247
+ hidden: identityColor,
248
+ strikethrough: identityColor,
249
+ black: identityColor,
250
+ red: identityColor,
251
+ green: identityColor,
252
+ yellow: identityColor,
253
+ blue: identityColor,
254
+ magenta: identityColor,
255
+ cyan: identityColor,
256
+ white: identityColor,
257
+ gray: identityColor,
258
+ bgBlack: identityColor,
259
+ bgRed: identityColor,
260
+ bgGreen: identityColor,
261
+ bgYellow: identityColor,
262
+ bgBlue: identityColor,
263
+ bgMagenta: identityColor,
264
+ bgCyan: identityColor,
265
+ bgWhite: identityColor,
266
+ blackBright: identityColor,
267
+ redBright: identityColor,
268
+ greenBright: identityColor,
269
+ yellowBright: identityColor,
270
+ blueBright: identityColor,
271
+ magentaBright: identityColor,
272
+ cyanBright: identityColor,
273
+ whiteBright: identityColor,
274
+ bgBlackBright: identityColor,
275
+ bgRedBright: identityColor,
276
+ bgGreenBright: identityColor,
277
+ bgYellowBright: identityColor,
278
+ bgBlueBright: identityColor,
279
+ bgMagentaBright: identityColor,
280
+ bgCyanBright: identityColor,
281
+ bgWhiteBright: identityColor
282
+ };
283
+ export const re = typedRe;
284
+ function refreshTypedRe() {
285
+ for (const colorName of Object.keys(typedRe)) {
286
+ typedRe[colorName] = identityColor;
287
+ }
288
+ for (const [k, fn] of Object.entries(colorFunctions)) {
289
+ typedRe[k] = fn;
290
+ }
291
+ }
292
+ function initColorFunctions() {
293
+ colorFunctions = {};
294
+ if (config.colorLevel === 0) {
295
+ for (const k of Object.keys(baseColors)) {
296
+ colorFunctions[k] = identityColor;
297
+ }
298
+ return;
299
+ }
300
+ for (const [key, [open, close, replace]] of Object.entries(colorMap)) {
301
+ colorFunctions[key] = createFormatter(open, close, replace ?? open);
302
+ }
303
+ }
304
+ function rebuild() {
305
+ colorMap = buildColorMap(config);
306
+ initColorFunctions();
307
+ refreshTypedRe();
308
+ }
309
+ rebuild();
310
+ export function configure(userInput) {
311
+ let newConfig;
312
+ if (typeof userInput === "object" && userInput !== null) {
313
+ newConfig = { ...config, ...userInput };
314
+ } else {
315
+ newConfig = { ...config };
316
+ }
317
+ config = newConfig;
318
+ rebuild();
319
+ }
320
+ export function getColor(name) {
321
+ const maybeFn = colorFunctions[name];
322
+ if (maybeFn) return maybeFn;
323
+ const resetFn = colorFunctions.reset;
324
+ if (resetFn) return resetFn;
325
+ return identityColor;
326
+ }
327
+ export function colorize(name, text) {
328
+ const fn = getColor(name);
329
+ return fn(text);
330
+ }
331
+ export function setColorLevel(level) {
332
+ configure({ colorLevel: level });
333
+ }
334
+ export function rgb(r, g, b) {
335
+ if (config.colorLevel === 3) {
336
+ const open = `\x1B[38;2;${r};${g};${b}m`;
337
+ const close = "\x1B[39m";
338
+ return createFormatter(open, close);
339
+ }
340
+ return identityColor;
341
+ }
342
+ export const colorSupport = {
343
+ isColorSupported: getConfig().colorLevel !== 0,
344
+ isForced,
345
+ isDisabled,
346
+ terminalName: getCurrentTerminalName()
347
+ };
348
+ export async function initUserConfig() {
349
+ try {
350
+ const { config: userConfig } = await loadConfig({ name: "relico" });
351
+ configure(userConfig);
352
+ } catch (err) {
353
+ console.warn("Failed to load user config via c12:", err);
354
+ }
355
+ }
356
+ export function defineConfig(config2) {
357
+ return config2;
358
+ }
359
+ function getConfig() {
360
+ return { ...config };
361
+ }
package/package.json CHANGED
@@ -1,81 +1,56 @@
1
1
  {
2
+ "dependencies": {
3
+ "@reliverse/runtime": "^1.0.3",
4
+ "bun-types": "^1.2.8",
5
+ "c12": "^3.0.2",
6
+ "pathe": "^2.0.3"
7
+ },
8
+ "description": "@reliverse/relico is doing its best to improve your terminal colorization experience by leveraging TypeScript and TypeBox for enhanced reliability and type safety.",
9
+ "homepage": "https://docs.reliverse.org",
10
+ "license": "MIT",
2
11
  "name": "@reliverse/relico",
3
- "version": "1.0.0",
4
- "author": "blefnk",
5
12
  "type": "module",
6
- "description": "@reliverse/relico is doing its best to improve your terminal colorization experience by leveraging TypeScript and TypeBox for enhanced reliability and type safety.",
7
- "scripts": {
8
- "dev": "bun examples/example.ts",
9
- "check": "bun typecheck && bun test && bun knip && bun lint && bun format",
10
- "build:npm": "unbuild && bun build.optim.ts",
11
- "build:jsr": "bun build.optim.ts --jsr",
12
- "build": "bun build:npm && bun build:jsr",
13
- "pub:npm": "bun build.publish.ts",
14
- "pub:jsr": "bun build.publish.ts --jsr",
15
- "pub:dry": "bun build.publish.ts --dry-run",
16
- "pub": "bun check && bun publish.ts",
17
- "typecheck": "tsc --noEmit",
18
- "lint": "eslint --cache --fix .",
19
- "lint:i": "eslint --inspect-config",
20
- "format": "biome check --write .",
21
- "attw": "bunx @arethetypeswrong/cli",
22
- "unpub": "npm unpublish",
23
- "test": "vitest",
24
- "knip": "knip",
25
- "latest": "bun update --latest"
26
- },
27
- "publishConfig": {
28
- "access": "public"
13
+ "version": "1.0.2",
14
+ "author": "reliverse",
15
+ "bugs": {
16
+ "email": "blefnk@gmail.com",
17
+ "url": "https://github.com/reliverse/relico/issues"
29
18
  },
19
+ "keywords": [
20
+ "reliverse"
21
+ ],
30
22
  "repository": {
31
23
  "type": "git",
32
24
  "url": "git+https://github.com/reliverse/relico.git"
33
25
  },
34
- "types": "./dist-npm/main.d.ts",
35
- "module": "./dist-npm/main.js",
36
- "main": "./dist-npm/main.js",
37
- "exports": {
38
- "import": "./dist-npm/main.js",
39
- "types": "./dist-npm/main.d.ts"
40
- },
41
- "bugs": {
42
- "url": "https://github.com/reliverse/relico/issues",
43
- "email": "blefnk@gmail.com"
44
- },
45
- "files": ["package.json", "README.md", "LICENSE.md", "dist-npm"],
46
- "homepage": "https://github.com/reliverse/relico",
47
- "keywords": ["cli", "reliverse"],
48
- "license": "MIT",
49
- "dependencies": {
50
- "@sinclair/typebox": "^0.34.14",
51
- "confbox": "^0.1.8",
52
- "destr": "^2.0.3",
53
- "fs-extra": "^11.3.0",
54
- "globby": "^14.0.2",
55
- "mri": "^1.2.0",
56
- "pathe": "^2.0.2",
57
- "std-env": "^3.8.0"
58
- },
59
26
  "devDependencies": {
60
- "@arethetypeswrong/cli": "^0.17.3",
61
27
  "@biomejs/biome": "1.9.4",
62
- "@eslint/js": "^9.18.0",
63
- "@eslint/json": "^0.9.1",
64
- "@eslint/markdown": "^6.2.1",
65
- "@stylistic/eslint-plugin": "^2.13.0",
66
- "@types/bun": "^1.1.18",
67
- "@types/eslint__js": "^8.42.3",
68
- "@types/fs-extra": "^11.0.4",
69
- "@types/node": "^22.10.7",
70
- "citty": "^0.1.6",
71
- "eslint": "^9.18.0",
72
- "eslint-plugin-perfectionist": "^4.7.0",
73
- "execa": "^9.5.2",
28
+ "@eslint/js": "^9.23.0",
29
+ "@reliverse/relidler-cfg": "^1.1.3",
30
+ "@stylistic/eslint-plugin": "^4.2.0",
31
+ "@types/bun": "^1.2.8",
32
+ "@types/node": "^22.13.14",
33
+ "eslint": "^9.23.0",
34
+ "eslint-plugin-no-relative-import-paths": "^1.6.1",
35
+ "eslint-plugin-perfectionist": "^4.10.1",
74
36
  "jiti": "^2.4.2",
75
- "knip": "^5.42.3",
76
- "typescript": "^5.7.3",
77
- "typescript-eslint": "^8.21.0",
78
- "unbuild": "^3.3.1",
79
- "vitest": "^3.0.3"
37
+ "knip": "^5.46.4",
38
+ "typescript": "^5.8.2",
39
+ "typescript-eslint": "^8.29.0",
40
+ "vitest": "^3.1.1"
41
+ },
42
+ "exports": {
43
+ ".": "./bin/main.js"
44
+ },
45
+ "files": [
46
+ "bin",
47
+ "package.json",
48
+ "README.md",
49
+ "LICENSE"
50
+ ],
51
+ "main": "./bin/main.js",
52
+ "module": "./bin/main.js",
53
+ "publishConfig": {
54
+ "access": "public"
80
55
  }
81
56
  }
@@ -1,80 +0,0 @@
1
- import type { Static } from "@sinclair/typebox";
2
- export declare function getCurrentTerminalName(): string;
3
- export declare const ColorDefinitionSchema: import("@sinclair/typebox").TTuple<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TString, import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>]>;
4
- export declare const ColorMapSchema: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TTuple<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TString, import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>]>>;
5
- export declare const RelicoConfigSchema: import("@sinclair/typebox").TObject<{
6
- colorLevel: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<0>, import("@sinclair/typebox").TLiteral<1>, import("@sinclair/typebox").TLiteral<2>, import("@sinclair/typebox").TLiteral<3>]>>;
7
- customColors: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TTuple<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TString, import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>]>>>;
8
- }>;
9
- export type RelicoConfig = Static<typeof RelicoConfigSchema>;
10
- /**
11
- * Configures the library with a partial or complete `RelicoConfig`.
12
- * For any invalid fields, a warning is shown and they are ignored.
13
- */
14
- export declare function configure(userInput: unknown): void;
15
- /** Returns a read-only copy of the current configuration. */
16
- export declare function getConfig(): RelicoConfig;
17
- /** Returns a color function by name (or `reset` or identity if not found). */
18
- export declare function getColor(name: string): (text: string | number) => string;
19
- /** Colorize text with a color function. */
20
- export declare function colorize(name: string, text: string | number): string;
21
- /** Set the color level (0=none,1=basic,2=256,3=truecolor). */
22
- export declare function setColorLevel(level: 0 | 1 | 2 | 3): void;
23
- /** Returns a custom "rgb" color function if level=3, otherwise identity. */
24
- export declare function rgb(r: number, g: number, b: number): (text: string | number) => string;
25
- export type IRelicoColors = {
26
- reset(text: string | number): string;
27
- bold(text: string | number): string;
28
- dim(text: string | number): string;
29
- italic(text: string | number): string;
30
- underline(text: string | number): string;
31
- inverse(text: string | number): string;
32
- hidden(text: string | number): string;
33
- strikethrough(text: string | number): string;
34
- black(text: string | number): string;
35
- red(text: string | number): string;
36
- green(text: string | number): string;
37
- yellow(text: string | number): string;
38
- blue(text: string | number): string;
39
- magenta(text: string | number): string;
40
- cyan(text: string | number): string;
41
- white(text: string | number): string;
42
- gray(text: string | number): string;
43
- bgBlack(text: string | number): string;
44
- bgRed(text: string | number): string;
45
- bgGreen(text: string | number): string;
46
- bgYellow(text: string | number): string;
47
- bgBlue(text: string | number): string;
48
- bgMagenta(text: string | number): string;
49
- bgCyan(text: string | number): string;
50
- bgWhite(text: string | number): string;
51
- blackBright(text: string | number): string;
52
- redBright(text: string | number): string;
53
- greenBright(text: string | number): string;
54
- yellowBright(text: string | number): string;
55
- blueBright(text: string | number): string;
56
- magentaBright(text: string | number): string;
57
- cyanBright(text: string | number): string;
58
- whiteBright(text: string | number): string;
59
- bgBlackBright(text: string | number): string;
60
- bgRedBright(text: string | number): string;
61
- bgGreenBright(text: string | number): string;
62
- bgYellowBright(text: string | number): string;
63
- bgBlueBright(text: string | number): string;
64
- bgMagentaBright(text: string | number): string;
65
- bgCyanBright(text: string | number): string;
66
- bgWhiteBright(text: string | number): string;
67
- [k: string]: (text: string | number) => string;
68
- };
69
- /**
70
- * The typed `re` object with all known color methods
71
- * plus user-defined ones (index signature).
72
- */
73
- export declare const re: IRelicoColors;
74
- export type ColorSupport = {
75
- isColorSupported: boolean;
76
- isForced: boolean;
77
- isDisabled: boolean;
78
- terminalName: string;
79
- };
80
- export declare const colorSupport: ColorSupport;
package/dist-npm/main.js DELETED
@@ -1,329 +0,0 @@
1
- import { Type } from "@sinclair/typebox";
2
- import { Value } from "@sinclair/typebox/value";
3
- import { env, isWindows, isLinux, isMacOS } from "std-env";
4
- export function getCurrentTerminalName() {
5
- const termProgram = env["TERM_PROGRAM"];
6
- const term = env["TERM"];
7
- const terminalEmulator = env["TERMINAL_EMULATOR"];
8
- if (termProgram) {
9
- switch (termProgram.toLowerCase()) {
10
- case "vscode":
11
- return "VSCode Terminal";
12
- case "terminus-sublime":
13
- return "Terminus Sublime";
14
- case "hyper":
15
- return "Hyper";
16
- case "iterm.app":
17
- case "iterm":
18
- return "iTerm2";
19
- case "alacritty":
20
- return "Alacritty";
21
- case "wezterm":
22
- return "WezTerm";
23
- case "terminus":
24
- return "Terminus";
25
- default:
26
- return `TERM_PROGRAM: ${termProgram}`;
27
- }
28
- }
29
- if (terminalEmulator) {
30
- switch (terminalEmulator.toLowerCase()) {
31
- case "jetbrains-jediterm":
32
- return "JetBrains JediTerm";
33
- case "cmder":
34
- return "Cmder";
35
- case "conemu":
36
- return "ConEmu";
37
- default:
38
- return `TERMINAL_EMULATOR: ${terminalEmulator}`;
39
- }
40
- }
41
- if (term) {
42
- const lowered = term.toLowerCase();
43
- switch (lowered) {
44
- case "xterm-256color":
45
- return "Xterm 256 Color";
46
- case "alacritty":
47
- return "Alacritty";
48
- case "xterm":
49
- return "Xterm";
50
- case "linux":
51
- return "Linux Console Kernel";
52
- default:
53
- return `TERM: ${term}`;
54
- }
55
- }
56
- if (isWindows) {
57
- return "Windows Terminal";
58
- } else if (isMacOS) {
59
- return "macOS Terminal";
60
- } else if (isLinux) {
61
- return "Linux Terminal";
62
- }
63
- return "Unknown Terminal";
64
- }
65
- export const ColorDefinitionSchema = Type.Tuple([
66
- Type.String(),
67
- Type.String(),
68
- Type.Optional(Type.String())
69
- ]);
70
- export const ColorMapSchema = Type.Record(Type.String(), ColorDefinitionSchema);
71
- export const RelicoConfigSchema = Type.Object(
72
- {
73
- colorLevel: Type.Optional(
74
- Type.Union([
75
- Type.Literal(0),
76
- Type.Literal(1),
77
- Type.Literal(2),
78
- Type.Literal(3)
79
- ])
80
- ),
81
- customColors: Type.Optional(ColorMapSchema)
82
- },
83
- { additionalProperties: false }
84
- );
85
- const argv = typeof process === "undefined" ? [] : process.argv;
86
- const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
87
- const isForced = "FORCE_COLOR" in env || argv.includes("--color");
88
- const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
89
- const isCompatibleTerminal = typeof process !== "undefined" && Boolean(process.stdout) && Boolean(process.stdout.isTTY) && env["TERM"] !== "dumb";
90
- const colorterm = (env["COLORTERM"] ?? "").toLowerCase();
91
- const supportsTrueColor = colorterm === "truecolor" || colorterm === "24bit";
92
- function detectColorLevel() {
93
- if (isDisabled) return 0;
94
- if (isForced) return 3;
95
- if (supportsTrueColor) return 3;
96
- if (isWindows) return 2;
97
- if (isCI) return 2;
98
- if (isCompatibleTerminal) {
99
- return 2;
100
- }
101
- return 0;
102
- }
103
- const baseColors = {
104
- reset: ["\x1B[0m", "\x1B[0m"],
105
- bold: ["\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"],
106
- dim: ["\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"],
107
- italic: ["\x1B[3m", "\x1B[23m"],
108
- underline: ["\x1B[4m", "\x1B[24m"],
109
- inverse: ["\x1B[7m", "\x1B[27m"],
110
- hidden: ["\x1B[8m", "\x1B[28m"],
111
- strikethrough: ["\x1B[9m", "\x1B[29m"],
112
- black: ["\x1B[30m", "\x1B[39m"],
113
- red: ["\x1B[31m", "\x1B[39m"],
114
- green: ["\x1B[32m", "\x1B[39m"],
115
- yellow: ["\x1B[33m", "\x1B[39m"],
116
- blue: ["\x1B[34m", "\x1B[39m"],
117
- magenta: ["\x1B[35m", "\x1B[39m"],
118
- cyan: ["\x1B[36m", "\x1B[39m"],
119
- white: ["\x1B[37m", "\x1B[39m"],
120
- gray: ["\x1B[90m", "\x1B[39m"],
121
- bgBlack: ["\x1B[40m", "\x1B[49m"],
122
- bgRed: ["\x1B[41m", "\x1B[49m"],
123
- bgGreen: ["\x1B[42m", "\x1B[49m"],
124
- bgYellow: ["\x1B[43m", "\x1B[49m"],
125
- bgBlue: ["\x1B[44m", "\x1B[49m"],
126
- bgMagenta: ["\x1B[45m", "\x1B[49m"],
127
- bgCyan: ["\x1B[46m", "\x1B[49m"],
128
- bgWhite: ["\x1B[47m", "\x1B[49m"],
129
- blackBright: ["\x1B[90m", "\x1B[39m"],
130
- redBright: ["\x1B[91m", "\x1B[39m"],
131
- greenBright: ["\x1B[92m", "\x1B[39m"],
132
- yellowBright: ["\x1B[93m", "\x1B[39m"],
133
- blueBright: ["\x1B[94m", "\x1B[39m"],
134
- magentaBright: ["\x1B[95m", "\x1B[39m"],
135
- cyanBright: ["\x1B[96m", "\x1B[39m"],
136
- whiteBright: ["\x1B[97m", "\x1B[39m"],
137
- bgBlackBright: ["\x1B[100m", "\x1B[49m"],
138
- bgRedBright: ["\x1B[101m", "\x1B[49m"],
139
- bgGreenBright: ["\x1B[102m", "\x1B[49m"],
140
- bgYellowBright: ["\x1B[103m", "\x1B[49m"],
141
- bgBlueBright: ["\x1B[104m", "\x1B[49m"],
142
- bgMagentaBright: ["\x1B[105m", "\x1B[49m"],
143
- bgCyanBright: ["\x1B[106m", "\x1B[49m"],
144
- bgWhiteBright: ["\x1B[107m", "\x1B[49m"]
145
- };
146
- const windowsTerminalColors = {
147
- ...baseColors,
148
- red: ["\x1B[38;2;255;85;85m", "\x1B[39m"],
149
- green: ["\x1B[38;2;80;250;123m", "\x1B[39m"],
150
- yellow: ["\x1B[38;2;241;250;140m", "\x1B[39m"],
151
- blue: ["\x1B[38;2;98;114;164m", "\x1B[39m"],
152
- magenta: ["\x1B[38;2;255;121;198m", "\x1B[39m"],
153
- cyan: ["\x1B[38;2;139;233;253m", "\x1B[39m"]
154
- };
155
- let config = {
156
- colorLevel: detectColorLevel()
157
- };
158
- let colorMap = {};
159
- let colorFunctions = {};
160
- function replaceClose(str, close, replace, index) {
161
- let result = "";
162
- let cursor = 0;
163
- let i = index;
164
- while (i !== -1) {
165
- result += str.substring(cursor, i) + replace;
166
- cursor = i + close.length;
167
- i = str.indexOf(close, cursor);
168
- }
169
- return result + str.substring(cursor);
170
- }
171
- function createFormatter(open, close, replace = open) {
172
- return (input) => {
173
- const stringed = String(input);
174
- const idx = stringed.indexOf(close, open.length);
175
- if (idx !== -1) {
176
- return open + replaceClose(stringed, close, replace, idx) + close;
177
- }
178
- return open + stringed + close;
179
- };
180
- }
181
- function buildColorMap(cfg) {
182
- const terminalName = getCurrentTerminalName();
183
- const isWinTerm = terminalName === "Windows Terminal";
184
- if (cfg.colorLevel === 0) {
185
- const map = {};
186
- for (const k of Object.keys(baseColors)) {
187
- map[k] = ["", "", ""];
188
- }
189
- return map;
190
- }
191
- let builtIn;
192
- if (isWinTerm && cfg.colorLevel === 3) {
193
- builtIn = { ...windowsTerminalColors };
194
- } else {
195
- builtIn = { ...baseColors };
196
- }
197
- if (cfg.customColors) {
198
- for (const [k, v] of Object.entries(cfg.customColors)) {
199
- builtIn[k] = v;
200
- }
201
- }
202
- return builtIn;
203
- }
204
- function initColorFunctions() {
205
- colorFunctions = {};
206
- if (config.colorLevel === 0) {
207
- for (const k of Object.keys(baseColors)) {
208
- colorFunctions[k] = identityColor;
209
- }
210
- return;
211
- }
212
- for (const [key, [open, close, replace]] of Object.entries(colorMap)) {
213
- colorFunctions[key] = createFormatter(open, close, replace ?? open);
214
- }
215
- }
216
- function rebuild() {
217
- colorMap = buildColorMap(config);
218
- initColorFunctions();
219
- }
220
- function identityColor(text) {
221
- return String(text);
222
- }
223
- rebuild();
224
- export function configure(userInput) {
225
- let newObj = null;
226
- if (typeof userInput === "object" && userInput !== null) {
227
- newObj = { ...config, ...userInput };
228
- } else {
229
- newObj = { ...config };
230
- }
231
- try {
232
- const parsed = Value.Cast(RelicoConfigSchema, newObj);
233
- config = parsed;
234
- } catch (err) {
235
- console.warn("Invalid relico config:", err);
236
- return;
237
- }
238
- rebuild();
239
- }
240
- export function getConfig() {
241
- return { ...config };
242
- }
243
- export function getColor(name) {
244
- const maybeFn = colorFunctions[name];
245
- if (maybeFn) return maybeFn;
246
- const resetFn = colorFunctions["reset"];
247
- if (resetFn) return resetFn;
248
- return identityColor;
249
- }
250
- export function colorize(name, text) {
251
- const fn = getColor(name);
252
- return fn(text);
253
- }
254
- export function setColorLevel(level) {
255
- configure({ colorLevel: level });
256
- }
257
- export function rgb(r, g, b) {
258
- if (config.colorLevel === 3) {
259
- const open = `\x1B[38;2;${String(r)};${String(g)};${String(b)}m`;
260
- const close = "\x1B[39m";
261
- return createFormatter(open, close);
262
- }
263
- return identityColor;
264
- }
265
- const typedRe = {
266
- reset: identityColor,
267
- bold: identityColor,
268
- dim: identityColor,
269
- italic: identityColor,
270
- underline: identityColor,
271
- inverse: identityColor,
272
- hidden: identityColor,
273
- strikethrough: identityColor,
274
- black: identityColor,
275
- red: identityColor,
276
- green: identityColor,
277
- yellow: identityColor,
278
- blue: identityColor,
279
- magenta: identityColor,
280
- cyan: identityColor,
281
- white: identityColor,
282
- gray: identityColor,
283
- bgBlack: identityColor,
284
- bgRed: identityColor,
285
- bgGreen: identityColor,
286
- bgYellow: identityColor,
287
- bgBlue: identityColor,
288
- bgMagenta: identityColor,
289
- bgCyan: identityColor,
290
- bgWhite: identityColor,
291
- blackBright: identityColor,
292
- redBright: identityColor,
293
- greenBright: identityColor,
294
- yellowBright: identityColor,
295
- blueBright: identityColor,
296
- magentaBright: identityColor,
297
- cyanBright: identityColor,
298
- whiteBright: identityColor,
299
- bgBlackBright: identityColor,
300
- bgRedBright: identityColor,
301
- bgGreenBright: identityColor,
302
- bgYellowBright: identityColor,
303
- bgBlueBright: identityColor,
304
- bgMagentaBright: identityColor,
305
- bgCyanBright: identityColor,
306
- bgWhiteBright: identityColor
307
- };
308
- function refreshTypedRe() {
309
- for (const colorName of Object.keys(typedRe)) {
310
- typedRe[colorName] = identityColor;
311
- }
312
- for (const [k, fn] of Object.entries(colorFunctions)) {
313
- typedRe[k] = fn;
314
- }
315
- }
316
- const originalRebuild = rebuild;
317
- function newRebuild() {
318
- originalRebuild();
319
- refreshTypedRe();
320
- }
321
- rebuild = newRebuild;
322
- newRebuild();
323
- export const re = typedRe;
324
- export const colorSupport = {
325
- isColorSupported: getConfig().colorLevel !== 0,
326
- isForced,
327
- isDisabled,
328
- terminalName: getCurrentTerminalName()
329
- };