diff --git a/.commitlintrc b/.commitlintrc new file mode 100644 index 0000000..c30e5a9 --- /dev/null +++ b/.commitlintrc @@ -0,0 +1,3 @@ +{ + "extends": ["@commitlint/config-conventional"] +} diff --git a/.czrc b/.czrc new file mode 100644 index 0000000..d1bcc20 --- /dev/null +++ b/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4a7ea30 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..0166703 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [xinyao27] diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..5c51a36 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: Lint + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install bun + uses: oven-sh/setup-bun@v1 + + - name: Install + run: bun install + + - name: Lint + run: bun run lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5d4d6f7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: Test + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v4 + + - name: Install bun + uses: oven-sh/setup-bun@v1 + + - name: Install + run: bun install + + - name: Build + run: bun run build + + - name: Test + run: bun run test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f88c4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# compiled output +dist +node_modules + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +coverage +/.nyc_output + +# IDEs and editors +.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# config +.env diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..36af219 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 0000000..fd8d9ec --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,6 @@ +{ + "*.{js?(x),ts?(x),vue,html,md,json,yml}": [ + "eslint --fix", + "git add" + ] +} diff --git a/.release-it.json b/.release-it.json new file mode 100644 index 0000000..3749272 --- /dev/null +++ b/.release-it.json @@ -0,0 +1,11 @@ +{ + "git": { + "commitMessage": "chore: release v${version}" + }, + "plugins": { + "@release-it/conventional-changelog": { + "preset": "angular", + "infile": "CHANGELOG.md" + } + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..11c711e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 xinyao27 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d24794a --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# xycolors 🌈 + +[![NPM version](https://img.shields.io/npm/v/xycolors?color=a1b858&label=)](https://www.npmjs.com/package/xycolors) + +> A tiny and fast package for adding colors to your terminal output. +> +> Based on [yoctocolors](https://github.com/sindresorhus/yoctocolors) + +## Highlights + +- Tiny +- Fast +- Handles nested colors +- Tree-shakeable +- No dependencies +- Actively maintained + +## Install + +```sh +npm install xycolors +``` + +## Usage + +```js +import * as c from 'xycolors' + +console.log(c.red('Yo!')) + +console.log(c.blue(`Welcome to the ${colors.magenta('xycolors')} package!`)) +``` + +_This package supports [basic color detection](https://nodejs.org/api/tty.html#writestreamhascolorscount-env). Colors can be forcefully enabled by setting the `FORCE_COLOR` environment variable to `1` and can be forcefully disabled by setting `NO_COLOR` or `NODE_DISABLE_COLORS` to any value. [More info.](https://nodejs.org/api/tty.html#writestreamgetcolordepthenv)_ + +## Styles + +### Modifiers + +- `reset` - Reset the current style. +- `bold` - Make the text bold. +- `dim` - Make the text have lower opacity. +- `italic` - Make the text italic. _(Not widely supported)_ +- `underline` - Put a horizontal line above the text. _(Not widely supported)_ +- `overline` - Put a horizontal line below the text. _(Not widely supported)_ +- `inverse`- Invert background and foreground colors. +- `hidden` - Print the text but make it invisible. +- `strikethrough` - Put a horizontal line through the center of the text. _(Not widely supported)_ + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `gray` +- `redBright` +- `greenBright` +- `yellowBright` +- `blueBright` +- `magentaBright` +- `cyanBright` +- `whiteBright` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` +- `bgGray` +- `bgRedBright` +- `bgGreenBright` +- `bgYellowBright` +- `bgBlueBright` +- `bgMagentaBright` +- `bgCyanBright` +- `bgWhiteBright` + +## Benchmark + +```sh +$ ./benchmark.js +┌─────────┬────────────────┬─────────────┐ +│ (index) │ library │ ops/sec │ +├─────────┼────────────────┼─────────────┤ +│ 0 │ 'xycolors' │ '8,000,000' │ +│ 1 │ 'picocolors' │ '8,000,000' │ +│ 2 │ 'colorette' │ '6,024,096' │ +│ 3 │ 'kleur/colors' │ '4,807,692' │ +│ 4 │ 'nanocolors' │ '4,807,692' │ +│ 5 │ 'chalk' │ '4,000,000' │ +│ 6 │ 'kleur' │ '4,000,000' │ +│ 7 │ 'ansi-colors' │ '1,848,429' │ +│ 8 │ 'cli-color' │ '585,480' │ +└─────────┴────────────────┴─────────────┘ +``` + +_See [benchmark.js](benchmark.js)._ + +## License + +[MIT](./LICENSE) License © 2024 [xinyao27](https://github.com/xinyao27) diff --git a/benchmark.js b/benchmark.js new file mode 100644 index 0000000..a632249 --- /dev/null +++ b/benchmark.js @@ -0,0 +1,82 @@ +#!/usr/bin/env node +import { Suite } from '@jonahsnider/benchmark' +import ansi from 'ansi-colors' +import chalk from 'chalk' +import cliColor from 'cli-color' +import * as colorette from 'colorette' +import kleur from 'kleur' +import * as kleurColors from 'kleur/colors' +import * as nanocolors from 'nanocolors' +import picocolors from 'picocolors' +import * as xycolors from './dist/index.mjs' + +const suite = new Suite('simple', { + warmup: { trials: 10_000_000 }, + run: { trials: 1_000_000 }, +}) + +// eslint-disable-next-line no-unused-vars +let out + +suite + .addTest('xycolors', () => { + out = xycolors.red('Add plugin to use time limit') + out = xycolors.green('Add plugin to use time limit') + out = xycolors.blue(`Add plugin to ${xycolors.cyan('use')} time limit`) + }) + .addTest('cli-color', () => { + out = cliColor.red('Add plugin to use time limit') + out = cliColor.green('Add plugin to use time limit') + out = cliColor.blue(`Add plugin to ${cliColor.cyan('use')} time limit`) + }) + .addTest('ansi-colors', () => { + out = ansi.red('Add plugin to use time limit') + out = ansi.green('Add plugin to use time limit') + out = ansi.blue(`Add plugin to ${ansi.cyan('use')} time limit`) + }) + .addTest('chalk', () => { + out = chalk.red('Add plugin to use time limit') + out = chalk.green('Add plugin to use time limit') + out = chalk.blue(`Add plugin to ${chalk.cyan('use')} time limit`) + }) + .addTest('kleur', () => { + out = kleur.red('Add plugin to use time limit') + out = kleur.green('Add plugin to use time limit') + out = kleur.blue(`Add plugin to ${kleur.cyan('use')} time limit`) + }) + .addTest('kleur/colors', () => { + out = kleurColors.red('Add plugin to use time limit') + out = kleurColors.green('Add plugin to use time limit') + out = kleurColors.blue(`Add plugin to ${kleurColors.cyan('use')} time limit`) + }) + .addTest('colorette', () => { + out = colorette.red('Add plugin to use time limit') + out = colorette.green('Add plugin to use time limit') + out = colorette.blue(`Add plugin to ${colorette.cyan('use')} time limit`) + }) + .addTest('nanocolors', () => { + out = nanocolors.red('Add plugin to use time limit') + out = nanocolors.green('Add plugin to use time limit') + out = nanocolors.blue(`Add plugin to ${nanocolors.cyan('use')} time limit`) + }) + .addTest('picocolors', () => { + out = picocolors.red('Add plugin to use time limit') + out = picocolors.green('Add plugin to use time limit') + out = picocolors.blue(`Add plugin to ${picocolors.cyan('use')} time limit`) + }) + +const results = await suite.run() + +const table = [...results] + // Convert median execution time to mean ops/sec + .map(([library, histogram]) => [library, Math.round(1e9 / histogram.percentile(50))]) + // Sort fastest to slowest + .sort(([, a], [, b]) => b - a) + // Convert to object for console.table + .map(([library, opsPerSec]) => ({ + library, + 'ops/sec': opsPerSec.toLocaleString(), + })) + +// eslint-disable-next-line no-console +console.table(table) diff --git a/build.config.ts b/build.config.ts new file mode 100644 index 0000000..6ab75ac --- /dev/null +++ b/build.config.ts @@ -0,0 +1,17 @@ +import { defineBuildConfig } from 'unbuild' +import pkg from './package.json' + +export default defineBuildConfig({ + entries: ['src/index'], + declaration: true, + clean: true, + rollup: { + emitCJS: true, + esbuild: { + minify: true, + }, + }, + replace: { + 'process.env.VERSION': JSON.stringify(pkg.version), + }, +}) diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..3414665 Binary files /dev/null and b/bun.lockb differ diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e0043d7 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,3 @@ +import { all } from '@xystack/style-guide/eslint' + +export default all diff --git a/package.json b/package.json new file mode 100644 index 0000000..9087cc0 --- /dev/null +++ b/package.json @@ -0,0 +1,94 @@ +{ + "name": "xycolors", + "version": "0.0.0", + "description": "A tiny and fast package for adding colors to your terminal output.", + "type": "module", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "ansi", + "style", + "styles", + "tty", + "formatting", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "homepage": "https://github.com/xinyao27/xycolors#readme", + "bugs": { + "url": "https://github.com/xinyao27/xycolors/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/xinyao27/xycolors.git" + }, + "author": { + "name": "xinyao", + "email": "hi@xinyao.me" + }, + "funding": "https://github.com/sponsors/xinyao27", + "files": [ + "dist/*" + ], + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.cjs", + "import": "./dist/index.mjs" + } + }, + "sideEffects": false, + "scripts": { + "build": "unbuild", + "stub": "unbuild --stub", + "typecheck": "tsc --noEmit", + "lint": "nr typecheck && eslint . --fix", + "prepublishOnly": "nr build", + "release": "release-it", + "start": "bun run src/index.ts", + "test": "bun test", + "preinstall": "npx only-allow bun", + "up": "taze major -I", + "prepare": "husky install", + "benchmark": "node ./benchmark.js" + }, + "devDependencies": { + "@antfu/ni": "^0.21.12", + "@commitlint/cli": "^19.3.0", + "@jonahsnider/benchmark": "^5.0.3", + "@release-it/conventional-changelog": "^8.0.1", + "@types/bun": "^1.1.2", + "@xystack/style-guide": "^0.1.0", + "ansi-colors": "^4.1.3", + "chalk": "^5.3.0", + "cli-color": "^2.0.4", + "colorette": "^2.0.20", + "commitizen": "^4.3.0", + "cz-conventional-changelog": "^3.3.0", + "eslint": "^9.2.0", + "husky": "^9.0.11", + "kleur": "^4.1.5", + "lint-staged": "^15.2.2", + "nanocolors": "^0.2.13", + "picocolors": "^1.0.1", + "prettier": "^3.2.5", + "release-it": "^17.2.1", + "taze": "^0.13.8", + "typescript": "^5.4.5", + "unbuild": "^2.0.0" + }, + "prettier": "@xystack/style-guide/prettier" +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..a6d0274 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,108 @@ +import { WriteStream } from 'node:tty' +import { hexToRgb } from './utils' +import { theme } from './theme' + +// TODO: Use a better method when it's added to Node.js (https://github.com/nodejs/node/pull/40240) +const hasColors = WriteStream.prototype.hasColors() +const closeCode = 39 +const bgCloseCode = 49 + +/** + * By https://github.com/sindresorhus/yoctocolors + * @param open + * @param close + * @returns + */ +const format = (open: number | string, close: number | string) => { + if (!hasColors) { + return (input: string) => input + } + + const openCode = `\u001B[${open}m` + const closeCode = `\u001B[${close}m` + + return (input: string) => { + const string = `${input}` + let index = string.indexOf(closeCode) + + if (index === -1) { + // Note: Intentionally not using string interpolation for performance reasons. + return openCode + string + closeCode + } + + // Handle nested colors. + + // We could have done this, but it's too slow (as of Node.js 22). + // return openCode + string.replaceAll(closeCode, openCode) + closeCode; + + let result = openCode + let lastIndex = 0 + + while (index !== -1) { + result += string.slice(lastIndex, index) + openCode + lastIndex = index + closeCode.length + index = string.indexOf(closeCode, lastIndex) + } + + result += string.slice(lastIndex) + closeCode + + return result + } +} +export const rgb = (r: number | string, g: number | string, b: number | string) => + format(`38;2;${r};${g};${b}`, closeCode) +export const bgRgb = (r: number | string, g: number | string, b: number | string) => + format(`48;2;${r};${g};${b}`, bgCloseCode) +const createHexFn = + (fn: (r: number | string, g: number | string, b: number | string) => (input: string) => string) => (hex: string) => { + const [r, g, b] = hexToRgb(hex) + return fn(r, g, b) + } +export const hex = createHexFn(rgb) +export const bgHex = createHexFn(bgRgb) + +export const reset = format(...theme.reset) +export const bold = format(...theme.bold) +export const dim = format(...theme.dim) +export const italic = format(...theme.italic) +export const underline = format(...theme.underline) +export const overline = format(...theme.overline) +export const inverse = format(...theme.inverse) +export const hidden = format(...theme.hidden) +export const strikethrough = format(...theme.strikethrough) + +export const black = format(...theme.black) +export const red = format(...theme.red) +export const green = format(...theme.green) +export const yellow = format(...theme.yellow) +export const blue = format(...theme.blue) +export const magenta = format(...theme.magenta) +export const cyan = format(...theme.cyan) +export const white = format(...theme.white) +export const gray = format(...theme.gray) + +export const bgBlack = format(...theme.bgBlack) +export const bgRed = format(...theme.bgRed) +export const bgGreen = format(...theme.bgGreen) +export const bgYellow = format(...theme.bgYellow) +export const bgBlue = format(...theme.bgBlue) +export const bgMagenta = format(...theme.bgMagenta) +export const bgCyan = format(...theme.bgCyan) +export const bgWhite = format(...theme.bgWhite) +export const bgGray = format(...theme.bgGray) + +export const redBright = format(...theme.redBright) +export const greenBright = format(...theme.greenBright) +export const yellowBright = format(...theme.yellowBright) +export const blueBright = format(...theme.blueBright) +export const magentaBright = format(...theme.magentaBright) +export const cyanBright = format(...theme.cyanBright) +export const whiteBright = format(...theme.whiteBright) + +export const bgRedBright = format(...theme.bgRedBright) +export const bgGreenBright = format(...theme.bgGreenBright) +export const bgYellowBright = format(...theme.bgYellowBright) +export const bgBlueBright = format(...theme.bgBlueBright) +export const bgMagentaBright = format(...theme.bgMagentaBright) +export const bgCyanBright = format(...theme.bgCyanBright) +export const bgWhiteBright = format(...theme.bgWhiteBright) diff --git a/src/theme.ts b/src/theme.ts new file mode 100644 index 0000000..0e01c4a --- /dev/null +++ b/src/theme.ts @@ -0,0 +1,47 @@ +export const theme: Record = { + reset: [0, 0], + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + overline: [53, 55], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29], + + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39], + + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + bgGray: [100, 49], + + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39], + + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49], +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..4a194b9 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,29 @@ +/** + * By https://github.com/webdiscus/ansis/blob/master/src/ansi-codes.js + * Convert hex color string to RGB values. + * + * A hexadecimal color code can be 3 or 6 digits with an optional "#" prefix. + * + * The 3 digits specifies an RGB doublet data as a fully opaque color. + * For example, "#123" specifies the color that is represented by "#112233". + * + * The 6 digits specifies a fully opaque color. + * For example, "#112233". + * + * @param {string} value A string that contains the hexadecimal RGB color representation. + * @return {[number, number, number]} The red, green, blue values in range [0, 255] . + */ +export const hexToRgb = (value: string) => { + let [, color] = /([\da-f]{3,6})/i.exec(value) || [] + const len = color ? color.length : 0 + + if (len === 3) { + color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2] + } else if (len !== 6) { + return [0, 0, 0] + } + + const num = Number.parseInt(color, 16) + + return [(num >> 16) & 255, (num >> 8) & 255, num & 255] +} diff --git a/test/fixture.js b/test/fixture.js new file mode 100644 index 0000000..59c6e99 --- /dev/null +++ b/test/fixture.js @@ -0,0 +1,4 @@ +import { red } from '../dist/index.mjs' + +// eslint-disable-next-line no-console +console.log(red('foo')) diff --git a/test/index.test.ts b/test/index.test.ts new file mode 100644 index 0000000..a4fb7c5 --- /dev/null +++ b/test/index.test.ts @@ -0,0 +1,72 @@ +/* eslint-disable no-console */ +import { exec } from 'node:child_process' +import { env } from 'node:process' +import { promisify } from 'node:util' +import { describe, expect, it } from 'bun:test' +import * as c from '../src/index' +import { theme } from '../src/theme' + +const pExec = promisify(exec) +const testColor = (startCode: number | string, endCode: number | string) => { + return `\u001B[${startCode}mfoo\u001B[${endCode}m` +} +const testRgb = (r: number, g: number, b: number) => { + return `\u001B[38;2;${r};${g};${b}mfoo\u001B[39m` +} +const testBgRgb = (r: number, g: number, b: number) => { + return `\u001B[48;2;${r};${g};${b}mfoo\u001B[49m` +} + +describe('colors', () => { + it(`theme`, () => { + Object.entries(theme).forEach(([key, [startCode, endCode]]) => { + if (key === 'rgb' || key === 'bgRgb' || key === 'hex' || key === 'bgHex') return + + // @ts-expect-error it's fine + const received = c[key]('foo') + console.log(`${key}:`, received) + const expected = testColor(startCode, endCode) + expect(received).toEqual(expected) + }) + }) + + it(`Nested colors`, () => { + const received = c.red(`Error: ${c.yellow('Warning')} continues in red`) + console.log(received) + expect(received).toEqual('\u001B[31mError: \u001B[33mWarning\u001B[31m continues in red\u001B[39m') + }) + + it(`Is noop when no colors are supported`, async () => { + const { stdout: received } = await pExec('node test/fixture.js', { env: { ...env, FORCE_COLOR: '0' } }) + console.log(received) + expect(received).toEqual('foo\n') + }) + + it(`rgb(255, 170, 153)`, () => { + const received = c.rgb(255, 170, 153)('foo') + console.log(received) + const expected = testRgb(255, 170, 153) + expect(received).toEqual(expected) + }) + + it(`bgRgb(255, 170, 153)`, () => { + const received = c.bgRgb(255, 170, 153)('foo') + console.log(received) + const expected = testBgRgb(255, 170, 153) + expect(received).toEqual(expected) + }) + + it(`hex('#ffaa99')`, () => { + const received = c.hex('#ffaa99')('foo') + console.log(received) + const expected = testRgb(255, 170, 153) + expect(received).toEqual(expected) + }) + + it(`bgHex('#ffaa99')`, () => { + const received = c.bgHex('#ffaa99')('foo') + console.log(received) + const expected = testBgRgb(255, 170, 153) + expect(received).toEqual(expected) + }) +}) diff --git a/test/utils.test.ts b/test/utils.test.ts new file mode 100644 index 0000000..a626b98 --- /dev/null +++ b/test/utils.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'bun:test' +import { hexToRgb } from '../src/utils' + +describe('convert HEX to RGB', () => { + it(`hexToRgb('FFAA99')`, () => { + const received = hexToRgb('FFAA99') + const expected = [255, 170, 153] + expect(received).toEqual(expected) + }) + + it(`hexToRgb('#FFAA99')`, () => { + const received = hexToRgb('#FFAA99') + const expected = [255, 170, 153] + expect(received).toEqual(expected) + }) + + it(`hexToRgb('#FA9')`, () => { + const received = hexToRgb('#FA9') + const expected = [255, 170, 153] + expect(received).toEqual(expected) + }) + + it(`hexToRgb('#FF99')`, () => { + const received = hexToRgb('#FF99') + const expected = [0, 0, 0] + expect(received).toEqual(expected) + }) + + it(`hexToRgb('something')`, () => { + const received = hexToRgb('something') + const expected = [0, 0, 0] + expect(received).toEqual(expected) + }) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..11d53fc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@xystack/style-guide/typescript" +}