npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ansi-to-pre

v1.0.6

Published

convert string with ANSI color codes into a styled html pre tag

Downloads

715

Readme

ansi-to-pre

Convert a string of terminal output with ANSI escape codes into a <pre> tag with styled spans (for putting in docs and web pages), or a flattened ANSI string with normalized color codes (for use with other ANSI-parsing programs that only support colors but not control codes).

Colors are supported of course, but so is the full suite of cursor motion, partial/full erasing, etc. Basically, everything that ink outputs, it will resolve and turn into an HTML or flattened ANSI representation.

USAGE

import { ansiToPre, ansiToAnsi } from 'ansi-to-pre'

// or however you get a colored ansi code string
import chalk from 'chalk'
const s = `
${chalk.red('red text')}
${chalk.blue('blue' + chalk.bold('bold'))}
`

console.log(ansiToPre(s))
/*
<pre style="background:#222222;color:#eeeeee;position:relative">
<span style="color:red">red text</span>
<span style="color:blue">blue</span><span style="color:blue;font-weight:bold">bold</span>
</pre>
*/

Emoji Fixing

Emojis are tricky to work with in JavaScript! Sometimes a single emoji glyph is one character wide, sometimes two, sometimes something in between. Regardless of CSS line-height or font-size, it might be taller than other characters in the line, causing weird spacing that makes your HTML not look like a faithful representation of the ANSI that printed to your terminal.

To address this irregularity, ansiToPre will replace all emoji characters with an absolutely positioned span, nudged up 0.3ex from its default position, followed by 2 spaces. In my testing, in most monospace fonts checked, this is about the best way to make the emoji show up in the right place in the line, with enough space to be shown fully. Occasionally, for narrower emojis, this can result in some extra space, but that's usually better than cutting them off or overlapping other characters.

Default Styling

By default, there are colors assigned to the root pre tag and the 16 basic ANSI colors, for a relatively basic dark theme.

To change the default colors or the theme, see the theme() method (and others) described below.

Hyperlinks

If there's a hyperlink in the ANSI string, using the OSC code \x1b]8;;<url>\x1b\, then it will be turned into an <a> tag in the HTML output.

You can also use terminal.setStyle({ href: '...' }) to write everything from that point forward as a hyperlink, and call .setStyle({ href: '' }) to stop writing in hyperlink mode.

Why?

I needed a way to put the output from tap reports into the documentation, and wasn't able to find an ANSI-to-HTML converter that handled the full set of cursor movements, erases, scrolling, etc. that a complicated ink program outputs. There are several that do colors, and a handful that do the other text styling like bold and underline, but none that handle cursor movement codes. So I was still doing a bunch of manual editing of the files before converting the styles to css, or cleaning up mochibake after conversion. No fun.

I called it ansi-to-pre, because dropping ANSI output into a <pre> tag is my main use case, but you could conceivably do a bunch of other interesting stuff with a virtual ANSI terminal, I guess. You could even use the Terminal class as a sort of chalk replacement that write to random places in the string of output, but to be honest, chalk has a much nicer API for most cases where you just want styled text, and ink is a better tool for complicated "write stuff to the terminal" tasks.

API

import {
  // main functions
  ansiToPre,
  ansiToAnsi,

  // classes
  Terminal,
  Block,
  Style,

  // other goodies
  theme,
  defaultColor,
  defaultBackground,
  nameCodes,
  codeNames,
  namedColors,
  namedBrightColors,
  xtermCode,
} from 'ansi-to-pre'

ansiToPre(ansi: string): string

Turn an ANSI string into a styled HTML <pre> tag.

ansiToAnsi(ansi: string): string

Resolve all control codes and return an ANSI color style representation of the result.

class Terminal

A representation of a virtual "terminal" screen where character and style information as the ANSI-encoded stream is parsed.

Important: this is not a full-fledged Stream class. You can write() to it multiple times, and it will update appropriately, but it does zero buffering or input validation, so writing a partial ANSI code sequence will result in mochibake in the output.

The virtual terminal is an infinitely high and wide screen, with no scrollback buffer. So, when scrollDown(n) is called (either explicitly, or with a \x1b[<n>S ANSI code), n lines are removed from the top of the "screen". When scrollUp(n) is called (explicitly or via a \x1b[<n>T ANSI code), n empty lines are added to the top of the screen; the lines lost to a previous scrollDown action are not restored when scrolling up.

Also, actions that move the cursor down or to the right, which would on a normal physical terminal be limited to the height/width of the terminal, are unbounded. For example, on an actual terminal, echo $'\x1b[1000Bhello' will print "hello" at the bottom of the screen (unless your screen happens to be more than 1000 lines high); in this virtual terminal, it will create 1000 empty lines.

Most of the methods (other than toString() of course) return this, allowing for things like this:

console.log(
  new Terminal()
    .setStyle({ color: '#ff0000' })
    .write('hello, ')
    .down(1)
    .setStyle({ inverse: true })
    .write('world!').ansi,
)

Outputs:

hello, world

constructor(input?: string)

Create a new Terminal instance. If provided, the string is written immediately.

property ansi: string

A representation of the current state of the Terminal as a normalized ANSI encoded string. All cursor codes resolved, and colors normalized to 48;2;r;g;b format.

property: blocks: Block[]

An array of Block objects each representing a string of text with a given style.

toString(): string

Returns an HTML representation of the text and styles as a pre tag with styled span children.

write(input: string): Terminal

Parse the ANSI encoded string provided, updating the internal character and style buffers appropriately.

setStyle(style: StyleProps | string): Terminal

Set the style that the terminal will use for text writes.

If a string, must be a valid \x1b[...m and/or \x1b]8;;<url>\x1b\ ANSI code.

The styles provided will be appended onto the current style in use, just as they would be by a real terminal if the relevant ANSI code is encountered.

up(n: number): Terminal

Move the cursor up n lines, stopping at the top.

down(n: number): Terminal

Move the cursor down n lines, without limit.

scrollUp(n: number): Terminal

Prepend n empty lines at the start of the buffer, effectively moving the cursor up as a result.

scrollDown(n: number): Terminal

Remove n lines from the start of the buffer, effectively moving the cursor down as a result.

forward(n): Terminal

Move the cursor forward n columns, without limit.

back(n): Terminal

Move the cursor back n columns, stopping at the first column.

nextLine(n): Terminal

Move to the start of the n-th next line.

prevLine(n): Terminal

Move to the start of the n-th previous line, stopping at the top of the screen.

horizontalPosition(n): Terminal

Move to the n-th column (1-indexed), limited by the left-most column.

position(row, column): Terminal

Move to the 1-indexed row and column specified, limited by the top and left sides of the screen.

eraseScreen(): Terminal

Delete all printed data from the screen.

Note that this is used for both \x1b[2J and \x1b[3J, because there is no scrollback buffer in this virtual terminal.

eraseScreenToEnd(): Terminal

Delete all printed data from the cursor to the end of the screen.

eraseScreenFromStart(): Terminal

Delete all printed data from the top of the screen to the cursor.

eraseLine(): Terminal

Delete the contents of the current line.

eraseLineToEnd(): Terminal

Delete printed data from the cursor to the end of the current line.

`eraseLineFromStart(): Terminal

Delete printed data from the start of the current line to the cursor.

class Block

A representation of a run of text in a given style.

write(text): Block

Append text to the block

property ansi (read-only)

A representation of the block as an ANSI styled string.

property text (read-only)

The unstyled text that will be written.

property style (read-only)

The Style object for this block of text.

class Style

An immutable representation of an ANSI style. Used by Terminal and Block to represent the styles in use for text to be printed.

If a Style object is created with the same properties as a formerly seen Style object, the same object will be returned.

For example:

const a = new Style({ bold: true })
const b = new Style({ bold: true })
assert.equal(a, b) // passes

This optimization cuts down considerably on object creation, because a Style is created for each styled character written to the Terminal buffer. It also means that Style objects can be compared directly with === to test for equivalence.

static propertiesAnsi(styles): string

Convert a set of properties to an ANSI style code

static ansiProperties(styles): string

Convert an ANSI style code to a set of properties

update(properties: Style | string): Style

Return a new Style with this one plus the updated properties.

If a string is provided, must be a valid \x1b[...m ANSI style code, though unrecognized properties within that code will be ignored rather than throwing an error.

toString(): string

A CSS string corresponding to the style properties set.

property isReset: boolean

True if this style is a full reset of all properties.

property ansi: string

A \x1b[...m ANSI code corresponding to this style.

defaultBackground(s?: string): string, defaultColor(s?: string): string

The hex codes set on the <pre> for background and color, and used as the "unstyled" colors.

Call with a string to change the color used.

type Names

Canonical names for the 8 standard colors.

type Theme

export type Theme = {
  defaultColor?: string
  defaultBackground?: string
  named?: { [name in Names]?: string }
  bright?: { [name in Names]?: string }
}

A representation of the theme used for the default color and background, 8 standard colors, and 8 bright variants.

theme: (t?: Theme): Theme

Call to get or set the color theme for the default color and background, 8 standard colors, and 8 bright variants.

Returns the theme in use.

For example:

import { theme } from 'ansi-to-pre'

// solarized dark with full-saturation brights
theme({
  defaultColor: '#000000',
  defaultBackground: '#eee8d5',
  // https://en.wikipedia.org/wiki/Solarized#Colors
  named: {
    black: '#002b36',
    red: '#dc322f',
    green: '#859900',
    yellow: '#b58900',
    blue: '#268bd2',
    magenta: '#d33682',
    cyan: '#2aa198',
    white: '#fdf6e3',
  },
  bright: {
    black: '#586e75',
    red: '#ff0000',
    green: '#00ff00',
    yellow: '#ffff00',
    blue: '#0000ff',
    magenta: '#ff00ff',
    cyan: '#00ffff',
    white: '#ffffff',
  },
})

nameCodes: { [name in Names]: NameCodes }

Mapping of the named colors to their ANSI bitwise grb numeric color codes. For example, nameCodes.black === 0.

codeNames: Names[]

A mapping of the codes 0-7 to their canonical names. For example, codeNames[0] === 'black'.

namedColors: string[]

Mapping of the named color codes to their HEX color represenations. May be modified to change theme.

namedBrightColors: string[]

Mapping of named color codes to the HEX color represenataions of their bright variants. May be modified to change theme.

This array may be modified to change the color scheme.

xtermCode: (n: number): string

Get the HEX code for an XTerm color code between 0 and 255. May be modified to change theme.