Skip to content

Commit a2b60be

Browse files
committed
sketch: shapers and colors
1 parent 382e67f commit a2b60be

File tree

14 files changed

+198
-152
lines changed

14 files changed

+198
-152
lines changed

β€Žs/colors/colorful.tsβ€Ž

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import {codes} from "./parts/codes.js"
3+
4+
export type Colors = typeof colorful
5+
6+
export const colorful = {
7+
none: (s: string) => s,
8+
...<{[key in keyof typeof codes]: (s: string) => string}>(
9+
Object.fromEntries(
10+
Object.entries(codes)
11+
.map(([key, code]) => [
12+
key,
13+
(s: string) => `${code}${s}${codes.reset}`,
14+
])
15+
)
16+
),
17+
}
18+

β€Žs/colors/colorless.tsβ€Ž

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
import {Colors} from "./colorful.js"
3+
import {codes} from "./parts/codes.js"
4+
5+
export const colorless = {
6+
none: (s: string) => s,
7+
...<{[key in keyof typeof codes]: (s: string) => string}>(
8+
Object.fromEntries(
9+
Object.entries(codes)
10+
.map(([key]) => [
11+
key,
12+
(s: string) => s,
13+
])
14+
)
15+
),
16+
} as Colors
17+

β€Žs/colors/parts/codes.tsβ€Ž

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
export const codes = Object.freeze({
3+
4+
// regular colors
5+
black: "\u001b[30m",
6+
red: "\u001b[31m",
7+
green: "\u001b[32m",
8+
yellow: "\u001b[33m",
9+
blue: "\u001b[34m",
10+
magenta: "\u001b[35m",
11+
cyan: "\u001b[36m",
12+
white: "\u001b[37m",
13+
14+
// bright colors
15+
brightBlack: "\u001b[90m",
16+
brightRed: "\u001b[91m",
17+
brightGreen: "\u001b[92m",
18+
brightYellow: "\u001b[93m",
19+
brightBlue: "\u001b[94m",
20+
brightMagenta: "\u001b[95m",
21+
brightCyan: "\u001b[96m",
22+
brightWhite: "\u001b[97m",
23+
24+
// background colors
25+
bgBlack: "\u001b[40m",
26+
bgRed: "\u001b[41m",
27+
bgGreen: "\u001b[42m",
28+
bgYellow: "\u001b[43m",
29+
bgBlue: "\u001b[44m",
30+
bgMagenta: "\u001b[45m",
31+
bgCyan: "\u001b[46m",
32+
bgWhite: "\u001b[47m",
33+
34+
// bright background colors
35+
bgBrightBlack: "\u001b[100m",
36+
bgBrightRed: "\u001b[101m",
37+
bgBrightGreen: "\u001b[102m",
38+
bgBrightYellow: "\u001b[103m",
39+
bgBrightBlue: "\u001b[104m",
40+
bgBrightMagenta: "\u001b[105m",
41+
bgBrightCyan: "\u001b[106m",
42+
bgBrightWhite: "\u001b[107m",
43+
44+
// styles
45+
bold: "\u001b[1m",
46+
dim: "\u001b[2m",
47+
italic: "\u001b[3m",
48+
underline: "\u001b[4m",
49+
inverse: "\u001b[7m",
50+
hidden: "\u001b[8m",
51+
strikethrough: "\u001b[9m",
52+
53+
// reset
54+
reset: "\u001b[0m",
55+
})
56+

β€Žs/colors/parts/color-fns.tsβ€Ž

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
import {codes} from "./codes.js"
3+
4+
export function uncolor(s: string) {
5+
return s.replace(
6+
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
7+
"",
8+
)
9+
}
10+
11+
export function colorHex(hex: string) {
12+
hex = hex.replace(/^#/, "")
13+
let bigint: number
14+
let r: number
15+
let g: number
16+
let b: number
17+
18+
if (hex.length === 3)
19+
bigint = parseInt(hex.split('').map(c => c + c).join(''), 16)
20+
else if (hex.length === 6)
21+
bigint = parseInt(hex, 16)
22+
else
23+
throw new Error('Invalid hex color')
24+
25+
r = (bigint >> 16) & 255
26+
g = (bigint >> 8) & 255
27+
b = bigint & 255
28+
return colorRgb(r, g, b)
29+
}
30+
31+
export function colorRgb(r: number, g: number, b: number) {
32+
const code = `\u001b[38;2;${r};${g};${b}m`
33+
return (s: string) => `${code}${s}${codes.reset}`
34+
}
35+
36+
export function colorBgRgb(r: number, g: number, b: number) {
37+
const code = `\u001b[48;2;${r};${g};${b}m`
38+
return (s: string) => `${code}${s}${codes.reset}`
39+
}
40+

β€Žs/example.test.tsβ€Ž

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,26 @@ await Science.run({
3232
}),
3333
}),
3434

35-
"transforms": Science.suite({
35+
"shapers": Science.suite({
3636
"custom prefix": test(async() => {
3737
const mock = new MockTarget()
3838
const logger = new Logger()
3939
.setTarget(mock)
40-
.setPalette(Logger.palettes.plain())
41-
.addTransform(() => ({
40+
.addShaper(() => ({
4241
stdout: items => ["stdout:", ...items],
4342
stderr: items => ["stderr:", ...items],
4443
}))
44+
45+
expect(mock.stdout.spy.calls.length).is(0)
46+
expect(mock.stderr.spy.calls.length).is(0)
47+
4548
logger.log("hello world!")
46-
console.log(mock.getSpyStdout(0))
4749
expect(mock.stdout.spy.calls.length).is(1)
4850
expect(mock.getSpyStdout(0)).is("stdout: hello world!")
51+
52+
logger.error("uh oh")
53+
expect(mock.stderr.spy.calls.length).is(1)
54+
expect(mock.getSpyStderr(0)).is("stderr: uh oh")
4955
}),
5056
}),
5157
})

β€Žs/logger.tsβ€Ž

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,51 @@
11

22
import {Target} from "./targets/target.js"
3+
import {Shaper} from "./shapers/shaper.js"
4+
import {denoTarget} from "./targets/deno.js"
35
import {nodeTarget} from "./targets/node.js"
4-
import {Palette} from "./palettes/palette.js"
5-
import {plainPalette} from "./palettes/plain.js"
6+
import {colorless} from "./colors/colorless.js"
67
import {consoleTarget} from "./targets/console.js"
7-
import {Transformer} from "./transforms/transform.js"
8-
import {timestampTransform} from "./transforms/timestamp.js"
8+
import {isColorSupported} from "./utils/supports.js"
9+
import {colorful, Colors} from "./colors/colorful.js"
10+
import {timestampShaper} from "./shapers/timestamp.js"
911

1012
export class Logger {
11-
static palettes = {
12-
plain: plainPalette,
13-
}
14-
1513
static targets = {
14+
deno: denoTarget,
1615
node: nodeTarget,
1716
console: consoleTarget,
1817
}
1918

20-
static transforms = {
21-
timestamp: timestampTransform,
19+
static colors = {
20+
colorful: () => colorful,
21+
colorless: () => colorless,
22+
auto: () => isColorSupported()
23+
? colorful
24+
: colorless,
25+
}
26+
27+
static shapers = {
28+
timestamp: timestampShaper,
2229
}
2330

24-
palette: Palette = {}
31+
colors: Colors = Logger.colors.auto()
2532
target = consoleTarget()
26-
transformers: Transformer[] = []
33+
shapers: Shaper[] = []
2734

2835
log(...items: any[]) {
29-
for (const transform of this.transformers)
36+
for (const transform of this.shapers)
3037
items = transform(this).stdout(items)
3138
this.target.stdout(items)
3239
}
3340

3441
error(...items: any[]) {
35-
for (const transform of this.transformers)
42+
for (const transform of this.shapers)
3643
items = transform(this).stderr(items)
3744
this.target.stderr(items)
3845
}
3946

40-
setPalette(palette: Palette) {
41-
this.palette = palette
47+
setColors(colors: Colors) {
48+
this.colors = colors
4249
return this
4350
}
4451

@@ -47,8 +54,8 @@ export class Logger {
4754
return this
4855
}
4956

50-
addTransform(transformer: Transformer) {
51-
this.transformers.push(transformer)
57+
addShaper(shaper: Shaper) {
58+
this.shapers.push(shaper)
5259
return this
5360
}
5461
}

β€Žs/palettes/palette.tsβ€Ž

Lines changed: 0 additions & 3 deletions
This file was deleted.

β€Žs/palettes/plain.tsβ€Ž

Lines changed: 0 additions & 5 deletions
This file was deleted.

β€Žs/parts/color.tsβ€Ž

Lines changed: 0 additions & 106 deletions
This file was deleted.

β€Žs/shapers/shaper.tsβ€Ž

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
import {Colors} from "../colors/colorful.js"
3+
4+
export type ShaperContext = {
5+
colors: Colors
6+
}
7+
8+
export type Shaper = (context: ShaperContext) => ShapeFns
9+
10+
export type ShapeFns = {
11+
stdout: (items: any[]) => any[]
12+
stderr: (items: any[]) => any[]
13+
}
14+

0 commit comments

Comments
Β (0)