Coder Social home page Coder Social logo

alexeyraspopov / picocolors Goto Github PK

View Code? Open in Web Editor NEW
1.2K 9.0 38.0 56 KB

The tiniest and the fastest library for terminal output formatting with ANSI colors

License: ISC License

JavaScript 96.51% TypeScript 3.49%
ansi colors console terminal zero-dependency

picocolors's Introduction

picocolors

The tiniest and the fastest library for terminal output formatting with ANSI colors.

import pc from "picocolors"

console.log(
  pc.green(`How are ${pc.italic(`you`)} doing?`)
)
  • No dependencies.
  • 14 times smaller and 2 times faster than chalk.
  • Used by popular tools like PostCSS, SVGO, Stylelint, and Browserslist.
  • Node.js v6+ & browsers support. Support for both CJS and ESM projects.
  • TypeScript type declarations included.
  • NO_COLOR friendly.

Motivation

With picocolors we are trying to draw attention to the node_modules size problem and promote performance-first culture.

Prior Art

Credits go to the following projects:

Benchmarks

The space in node_modules including sub-dependencies:

$ node ./benchmarks/size.js
Data from packagephobia.com
  chalk       101 kB
  cli-color  1249 kB
  ansi-colors  25 kB
  kleur        21 kB
  colorette    17 kB
  nanocolors   16 kB
+ picocolors    7 kB

Library loading time:

$ node ./benchmarks/loading.js
  chalk          6.167 ms
  cli-color     31.431 ms
  ansi-colors    1.585 ms
  kleur          2.008 ms
  kleur/colors   0.773 ms
  colorette      2.476 ms
  nanocolors     0.833 ms
+ picocolors     0.466 ms

Benchmark for simple use case:

$ node ./benchmarks/simple.js
  chalk         24,066,342 ops/sec
  cli-color        938,700 ops/sec
  ansi-colors    4,532,542 ops/sec
  kleur         20,343,122 ops/sec
  kleur/colors  35,415,770 ops/sec
  colorette     34,244,834 ops/sec
  nanocolors    33,443,265 ops/sec
+ picocolors    33,271,645 ops/sec

Benchmark for complex use cases:

$ node ./benchmarks/complex.js
  chalk            969,915 ops/sec
  cli-color        131,639 ops/sec
  ansi-colors      342,250 ops/sec
  kleur            611,880 ops/sec
  kleur/colors   1,129,526 ops/sec
  colorette      1,747,277 ops/sec
  nanocolors     1,251,312 ops/sec
+ picocolors     2,024,086 ops/sec

Usage

Picocolors provides an object which includes a variety of text coloring and formatting functions

import pc from "picocolors"

The object includes following coloring functions: black, red, green, yellow, blue, magenta, cyan, white, gray.

console.log(`I see a ${pc.red("red door")} and I want it painted ${pc.black("black")}`)

The object also includes following background color modifier functions: bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite.

console.log(
  pc.bgBlack(
    pc.white(`Tom appeared on the sidewalk with a bucket of whitewash and a long-handled brush.`)
  )
)

Besides colors, the object includes following formatting functions: dim, bold, hidden, italic, underline, strikethrough, reset, inverse.

for (let task of tasks) {
  console.log(`${pc.bold(task.name)} ${pc.dim(task.durationMs + "ms")}`)
}

The library provides additional utilities to ensure the best results for the task:

  • isColorSupported — boolean, explicitly tells whether or not the colors or formatting appear on the screen

    import pc from "picocolors"
    
    if (pc.isColorSupported) {
      console.log("Yay! This script can use colors and formatters")
    }
  • createColors(enabled) — a function that returns a new API object with manually defined color support configuration

    import pc from "picocolors"
    
    let { red, bgWhite } = pc.createColors(options.enableColors)

Replacing chalk

  1. Replace package name in import:

    - import chalk from 'chalk'
    + import pico from 'picocolors'
  2. Replace variable:

    - chalk.red(text)
    + pico.red(text)
  3. Replace chains to nested calls:

    - chalk.red.bold(text)
    + pico.red(pico.bold(text))
  4. You can use colorize-template to replace chalk’s tagged template literal.

    + import { createColorize } from 'colorize-template'
    
    + let colorize = createColorize(pico)
    - chalk.red.bold`full {yellow ${"text"}}`
    + colorize`{red.bold full {yellow ${"text"}}}`

picocolors's People

Contributors

ai avatar alexeyraspopov avatar borodean avatar chooks22 avatar marco-carvalho avatar usmanyunusov avatar zemlanin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

picocolors's Issues

Add ChangeLog

ChangeLog is very important for utility package. When developer will see picocolors updates in yarn upgrade-interactive (especially major update), they will need a quick and simple way to find differences and update safety.

Colorette benchmark results are outdated

Hi! We've made some performance optimizations lately, so I believe that numbers as listed currently in readme are no longer accurate. Would you consider recalculating them with the latest version of Colorette?

Also it would probably be helpful to list versions for all the libraries mentioned in the benchmark for fairness.

Colors do not work in browser console

The readme claims browser support; however it seems this is a bit of a bait-and-switch. The .browser.js version does nothing and is hardcoded for every function just to return strings unchanged. Looking through the previous issues I understand the reasoning for this, which is to support certain bundlers which were throwing errors.

Working version

Here is a picocolors.ts that works in the Chrome dev console. The only thing I've changed is to delete the isColorSupported function at the top which uses the Node tty module. Also I converted it to Typescript so I can easily move it between projects in a single file.

// picocolors.ts

const formatter =
  (open: string, close: string, replace = open) =>
  (input: string | number | null | undefined) => {
    const string = '' + input
    const index = string.indexOf(close, open.length)
    return ~index
      ? open + replaceClose(string, close, replace, index) + close
      : open + string + close
  }

const replaceClose = (
  string: string,
  close: string,
  replace: string,
  index: number
): string => {
  const start = string.substring(0, index) + replace
  const end = string.substring(index + close.length)
  const nextIndex = end.indexOf(close)
  return ~nextIndex
    ? start + replaceClose(end, close, replace, nextIndex)
    : start + end
}

let createColors = (enabled = true) => ({
  isColorSupported: enabled,
  reset: enabled ? (s: string) => `\x1b[0m${s}\x1b[0m` : String,
  bold: enabled ? formatter('\x1b[1m', '\x1b[22m', '\x1b[22m\x1b[1m') : String,
  dim: enabled ? formatter('\x1b[2m', '\x1b[22m', '\x1b[22m\x1b[2m') : String,
  italic: enabled ? formatter('\x1b[3m', '\x1b[23m') : String,
  underline: enabled ? formatter('\x1b[4m', '\x1b[24m') : String,
  inverse: enabled ? formatter('\x1b[7m', '\x1b[27m') : String,
  hidden: enabled ? formatter('\x1b[8m', '\x1b[28m') : String,
  strikethrough: enabled ? formatter('\x1b[9m', '\x1b[29m') : String,
  black: enabled ? formatter('\x1b[30m', '\x1b[39m') : String,
  red: enabled ? formatter('\x1b[31m', '\x1b[39m') : String,
  green: enabled ? formatter('\x1b[32m', '\x1b[39m') : String,
  yellow: enabled ? formatter('\x1b[33m', '\x1b[39m') : String,
  blue: enabled ? formatter('\x1b[34m', '\x1b[39m') : String,
  magenta: enabled ? formatter('\x1b[35m', '\x1b[39m') : String,
  cyan: enabled ? formatter('\x1b[36m', '\x1b[39m') : String,
  white: enabled ? formatter('\x1b[37m', '\x1b[39m') : String,
  gray: enabled ? formatter('\x1b[90m', '\x1b[39m') : String,
  bgBlack: enabled ? formatter('\x1b[40m', '\x1b[49m') : String,
  bgRed: enabled ? formatter('\x1b[41m', '\x1b[49m') : String,
  bgGreen: enabled ? formatter('\x1b[42m', '\x1b[49m') : String,
  bgYellow: enabled ? formatter('\x1b[43m', '\x1b[49m') : String,
  bgBlue: enabled ? formatter('\x1b[44m', '\x1b[49m') : String,
  bgMagenta: enabled ? formatter('\x1b[45m', '\x1b[49m') : String,
  bgCyan: enabled ? formatter('\x1b[46m', '\x1b[49m') : String,
  bgWhite: enabled ? formatter('\x1b[47m', '\x1b[49m') : String,
})

export default createColors()

I would happily open a PR to replace the stub version with this (or the JS equivalent), assuming this project is not abandoned...

Loading time benchmark is inaccurate

Library loading time depends on the order and list of modules that loaded in benchmark/loading-runner.js

If I leave only chalk, kleur/colors, colorette and picocolors then loading times are:

~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          7.546 ms
  kleur/colors   2.442 ms
  colorette      2.746 ms
+ picocolors     0.819 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          7.926 ms
  kleur/colors   2.532 ms
  colorette      2.900 ms
+ picocolors     0.867 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          7.351 ms
  kleur/colors   2.344 ms
  colorette      2.743 ms
+ picocolors     0.811 ms

but simply adding nanocolors shaves off 0.1 ms:

~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          7.178 ms
  kleur/colors   2.304 ms
  colorette      2.694 ms
  nanocolors     1.200 ms
+ picocolors     0.720 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          7.174 ms
  kleur/colors   2.337 ms
  colorette      2.715 ms
  nanocolors     1.227 ms
+ picocolors     0.711 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          6.862 ms
  kleur/colors   2.183 ms
  colorette      2.563 ms
  nanocolors     1.136 ms
+ picocolors     0.685 ms

and putting picocolors right after kleur/colors will double loading time (also see that colorette time drops)

~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          6.831 ms
  kleur/colors   2.195 ms
+ picocolors     1.499 ms
  colorette      1.767 ms
  nanocolors     1.023 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          6.688 ms
  kleur/colors   2.133 ms
+ picocolors     1.486 ms
  colorette      1.702 ms
  nanocolors     1.027 ms
~/picocolors ⬢ v16 ₪ main*= $ node benchmarks/loading
  chalk          6.720 ms
  kleur/colors   2.185 ms
+ picocolors     1.511 ms
  colorette      1.767 ms
  nanocolors     1.027 ms

Question: How to type "a color"?

This is just running fine:

const DEFAULT_COLORS: [string, string, string] = ['white', 'yellow', 'red'];
console.log(pc[DEFAULT_COLORS[1]]('sad'));

https://codesandbox.io/p/sandbox/picocolors-types-29zfg9?file=%2Fsrc%2Findex.ts%3A11%2C1

But how to type this correct? I'm too stupid, sorry.
For DEFAULT_COLORS[1]
I get this:

// Element implicitly has an 'any' type because expression of
// type 'string' can't be used to indextype
// 'Colors & { createColors: (enabled?: boolean | undefined) => Colors; }'.
//  No index signature with a parameter of type 'string' was
// found on type 'Colors & { createColors: (enabled?: boolean | undefined) => Colors; }'.

Problem is that a user can just overwrite colors by using own colors handed in as strings. So I'm trying to stick to that.

Benchmarks are misleading

For chalk, ansi-colors, and cli-color, the benchmarks use dot-notation for "nested" colors:

These libraries can obviously do the same thing as your library and should be benchmarked without using dot notation, like: https://github.com/alexeyraspopov/picocolors/blob/main/benchmarks/complex.js#L117.

Add Node.js 6 to CI

Don’t want to scary you, but current Babel need Node.js 6 support.

Browserslist need very old Node.js as well because we can’t do major releases often to clean old Node.js versions.

Request: Check for `--force-color` instead of `--color`

Existing programs that want to migrate to this library may have a --color option that takes an argument, such as --color none or --color auto. The natural implementation of --color auto would be to simply do nothing and let picocolors decide if colors should be on. But because the --color flag is present in process.argv (even though its intention was different), this line will always force colors on, so implementing --color auto is impossible. Unless you do the detection yourself, which kind of defeats the point.

The env variables are already called NO_COLOR and FORCE_COLOR; I think it would be least surprising for the options to also be --no-color and --force-color. Those are pretty unambiguous.

Colors are always enabled in CI, even when output is piped

I wrote a program that uses this module. The tests pass on my local machine (because the colours are sensibly disabled when the program's output is piped), but they fail in CI, because this line turns colours on no matter what if process.env.CI is set. Even if it's not in a TTY.

When stdout is not a TTY, I think colour should always be disabled, unless it's specifically forced on with FORCE_COLOR.

Can't use named ESM imports

Using a named import doesn't work at all, only default imports

import { bold, yellow, blue, red } from 'picocolors';
// SyntaxError: The requested module 'picocolors' does not provide an export named 'blue'

import picocolors from 'picocolors';
picocolors.blue // all fine

I'm using node v18.4.0

Add API to manually change color mode

PostCSS, Logux Server and some other Nano Colors users have own options to change colors.

Can we have a way to manually change support mode like:

import { createColor } from 'picocolors'

let { red } = createColor(options.color)

Or:

import { setColors, isColorSupported } from 'picocolors'

setColors(options.color ?? isColorSupported)

Be ready for numbers, `undefined` and `null`

Default JS is not type safe. It is very easy to make a mistake and send number to red(result).

It will be much easy to migrate projects if types will support at least numbers and code will be ready for numbers, undefined and null.

Build impossible with rollup and typescript

Hi !

On a TS project using picocolors and rollup as builder, since the update of some packages (@rollup/plugin-typescript, and rollup) the project just cannot build when picocolors is imported. I found a fix where I add an include option to the ts plugin of rollup but the solution is not really satisfying.

Here is a reproduction repository where I just kept the bare minimum : https://github.com/elturpin/test-rollup-ts-picocolors

I think that the problem come from some interaction with TS rollup plugin and picocolors because if I try with other packages (chalk, express ...) their is no problem. However i have completely no idea on why their is a problem ...

Maybe someone can solve this ?

Add tests

We need tests to avoid regressions.

Feel free to copy Nano Colors tests (they were written from scratch, I didn’t copy them from anywhere).

Avoid forcing allowSyntheticDefaultImports flag

When compiling TS, following errors is thrown:

    5 export = picocolors
      ~~~~~~~~~~~~~~~~~~~
    This module is declared with 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.

This makes picocolors not an ideal transitive dependency, as it forces dependant projects to use specific TS configuration, which they may want to avoid.

Are there any strong reasons why export = picocolors export style is used?

Больше чем один параметр для функции formatter

Так как console.log умеет принимать больше чем один параметр

console.log('Следующий год' , 2022, 'от рождества христова')

то и с picocolors я и по привычке пишу что-то такое:

console.log(pc.red('Следующий год' , 2022, 'от рождества христова'))

и в консоли получаю в результате только строчку Следующий год.

Хотелось бы, чтобы picocolors умел в более чем один параметр

Update benchmarks for Chalk (and other packages)

Chalk has got a new version, which is now also dependency-less, lightweight, and fast. cli-color and nanocolors also had updates. In other words, the benchmarks are now very inaccurate :)

A problem one might encounter is that Chalk is now ESM-only, which makes it impossible to use in the current benchmark setup. I've tried a basic bundle using ESBuild, and it works. Here's how:

  1. Clone chalk/chalk
  2. In source/index.js, replace
    • #ansi-styles with ./vendor/ansi-styles/index.js
    • #supports-color with ./vendor/supports-color/index.js
  3. Run npx esbuild source/index.js --outfile=dist/chalk.js --bundle --platform=node
  4. Copy dist/chalk.js to picocolors' repo under benchmarks/chalk.js
  5. Inside ./benchmarks, replace across all files
    • let chalk = require("chalk") with let chalk = require("./chalk").default

I could run the benchmarks on my machine, but it's slower than the one used for README, so I'm not sure if it makes sense for me to send a PR with this data...


P. S. Since many packages still use Chalk v4, I propose adding another line to the benchmarks:

$ node ./benchmarks/size.js 
Data from packagephobia.com
  chalk@4     101 kB
  chalk@5      41 kB
  cli-color   984 kB
  ansi-colors  25 kB
  kleur        21 kB
  colorette    17 kB
  nanocolors   15 kB
+ picocolors    7 kB

Missing colors

I'm not sure if the aim of this project is feature-parity with chalk, but since there's a migration guide and a benchmark comparison, this is probably the case. There are some colors missing in this project that are supported in chalk:

  1. bgGray
  2. all bright colors
    • blackBright / bgBlackBright
    • redBright / bgRedBright
    • greenBright / bgGreenBright
    • yellowBright / bgYellowBright
    • blueBright / bgBlueBright
    • magentaBright / bgMagentaBright
    • cyanBright / bgCyanBright
    • whiteBright / bgWhiteBright

The first looks like an oversight to me, since gray is available. I'm not sure about the bright colors though, otherwise I would have submitted a PR. Are you interested in supporting the full range of colors available in chalk?

Integration tests

What do you think if tests will print it’s value to terminal to check the result by eyes?

let str = 
console.log(str)
asset.equal(str, ideal)

It is just idea, tests are good even without it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.