import type { Color as ColorType } from 'culori' import { converter, formatHex, formatHex8, parse } from 'culori' import type { CssColor, UnresolvedColor } from 'lightningcss' import { Logger } from '../logger' import type { ProcessorBuilder } from './processor' import { pipe } from './utils' export class Color { private toRgb = converter('rgb') private readonly black = '#000000' private readonly logger = new Logger('Color') constructor(private readonly Processor: ProcessorBuilder) {} processColor(color: CssColor | UnresolvedColor) { if (typeof color === 'string') { const parsed = parse(color) if (parsed === undefined) { const colorFunction = color.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/)?.[1] if (colorFunction === undefined) { this.logger.error(`Failed to convert color ${color}`) return this.black } const colorValue = color.replace(colorFunction, '').slice(1, -1) return `rt.parseColor("${colorFunction}", ${colorValue})` } return this.format(parsed) } try { if (color.type === 'currentcolor') { return 'vars["currentColor"]?.(vars)' } if (color.type === 'rgb' || color.type === 'srgb') { const alpha = typeof color.alpha === 'number' ? color.alpha : pipe(color.alpha)( x => this.Processor.CSS.processValue(x), Number, x => isNaN(x) ? 1 : x, ) return this.format({ r: color.r / 255, g: color.g / 255, b: color.b / 255, alpha, mode: 'rgb', }) } const colorType = color.type === 'display-p3' ? 'p3' : color.type const result = this.toRgb({ mode: colorType, ...color, } as ColorType) return this.format(result) } catch { this.logger.error(`Failed to convert color ${JSON.stringify(color)}`) return this.black } } isColor(value: unknown): value is CssColor { return typeof value === 'string' && parse(value) !== undefined } private format(color: ColorType) { if (color.alpha === 1 || color.alpha === undefined) { return formatHex(color) } return formatHex8(color) } }