Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

hitext

hitext513MIT1.0.0-beta.1

Text decoration done right

text, decoration

readme

HiText logo

HiText

NPM version Build Status Coverage Status

HiText is a basis for a text (source code) decoration. The main goal is to provide a universal way to combine libraries decorating text and an output in the required format (HTML, TTY etc). The only prerequsites you need are a source text and set of ranges with some options, the rest will be done by HiText.

Why?

Just imagine you want to output in HTML a JavaScript code with syntax highlighting and to spotlight some fragments of it. You can use a library for syntax highlighting, that's not a problem. For fragment spotlighting you can get substrings and wrap them into HTML tags. Both operations are not so hard, but the problem is to combine results of them. Each operation adds HTML tags to a result, so another operation will not perform as expected because of HTML tags.

The solution is to change such operations to produce a set of ranges instead of markup. Once ranges is generated, HiText can merge them by universal rules and get a stream of segments. As a final step, segment stream is used to generate open and close tags when needed.

The approach allows to combine any number of decorators (which became range generators). Moreover, using a source and a range set, it's possible to output a result in any format beside HTML. An output format is defined by a printer.

Features

  • Universal way to combine a number of decorators (generators)
  • Much easier to make a generator
  • Parsers with formatting losses can be used. No AST mutations required to decorate an input
  • Flexible setup for an output. Build-in printers: HTML, TTY (terminal)

Example

const hitext = require('hitext');
const prism = require('hitext-prism');
const spotlightRanges = [[6, 11], [19, 24]];
const spotlightPrinter = {
    html: {
        open() { return '<span class="spotlight">'; },
        close() { return '</span>'; }
    }
}
const lineNumber = {
    ranges: hitext.gen.lines,
    printer: {
        html: {
            open({ line }) { return line + ' | ' }
        }
    }
};

// 1. Create pipeline
const highlightJsAndSpotlight = hitext([
    prism('js'),
    [spotlightRanges, spotlightPrinter],
    lineNumber
], 'html');
// or
const highlightJsAndSpotlight = hitext([prism('js')], 'html')
    .use(spotlightRanges, spotlightPrinter)
    .use(lineNumber);
// or
const highlightJsAndSpotlight = hitext
    .use(prism('js'))
    .use({
        ranges: spotlightRanges,
        printer: spotlightPrinter
    })
    .use(lineNumber)
    .printer('html');

// 2. Apply to a text
console.log(highlightJsAndSpotlight('const a = 1;\nconst b = 2;'));

Output:

1 | <span class="token keyword">const</span> <span class="spotlight">a <span class="token operator">=</span> <span class="token number">1</span></span><span class="token punctuation">;</span>
2 | <span class="token keyword">const</span> <span class="spotlight">b <span class="token operator">=</span> <span class="token number">2</span></span><span class="token punctuation">;</span>

Build-in generators

lines

const hitext = require('hitext');

console.log(
    hitext()
        .use(hitext.gen.lines, {
            html: {
                open: ({ line }) => `<span title="line #${line}">`,
                close: () => '</span>'
            }
        })
        .print('foo\nbar', 'html')
);
// '<span title="line #1">foo\n</span><span title="line #2">foo</span>'

lineContents

const hitext = require('hitext');

console.log(
    hitext()
        .use(hitext.gen.lineContents, {
            html: {
                open: ({ line }) => `<span title="line #${line}">`,
                close: () => '</span>'
            }
        })
        .print('foo\nbar', 'html')
);
// '<span title="line #1">foo</span>\n<span title="line #2">foo</span>'

newlines

const hitext = require('hitext');

console.log(
    hitext()
        .use(hitext.gen.newlines, {
            html: {
                open: ({ line }) => `<span title="line #${line}">`,
                close: () => '</span>'
            }
        })
        .print('foo\nbar', 'html')
);
// 'foo<span title="line #1">\n</span>foo'

matches(pattern)

const hitext = require('hitext');
const matchPrinter = {
    html: {
        open: () => `<span class="match">`,
        close: () => '</span>'
    }
};

console.log(
    hitext()
        .use(hitext.gen.matches('world'), matchPrinter)
        .print('Hello world! Hello world!', 'html')
);
// Hello <span class="match">world</span>! Hello <span class="match">world</span>!

console.log(
    hitext()
        .use(hitext.gen.matches(/\w+/), matchPrinter)
        .print('Hello world!', 'html')
);
// <span class="match">Hello</span> <span class="match">world</span>!

Build-in printers

html

hitext
    .use(whatever, {
        html: {
            open: () => '<span class="example">',
            close: () => '</span>'
        }
    })
    .printer('html');

tty

hitext
    .use(whatever, {
        tty({ createStyle }) => createStyle('bgWhite', 'red')
    })
    .use(whatever, {
        tty({ createStyleMap }) => createStyleMap({
            foo: 'green',
            bar: ['bgWhite', 'red']
        })
    })
    .use(whatever, {
        tty({ createStyleMap }) => createStyleMap({
            foo: 'green',
            bar: ['bgWhite', 'red']
        }, ({ data }) => {
            // specify the way how to transform data to map key
            // when this argument is not specified `({ data }) => data` is using
            return data.magickField;
        })
    })
    .printer('tty');

License

MIT

changelog

1.0.0-beta.1 (May 14, 2020)

  • Added hitext.printer.fork() method to extend/overload a printer set
  • Changed hitext() pipeline to return a decorate function (i.e. hitext(...).use(...).decorate(...) -> hitext(...).use(...)(...))
  • Added second argument (printer) to hitext().use() that overrides decorator's default printer setup (e.x. hitext().use(decorator, { html: { ... } }))
  • Renamed printer's methods: start -> open, finish -> close
  • Removed default <div> wrapper for html printer
  • Removed from export: generateRanges, resolvePrinter, print, finalize, decorate and utils
  • Changed semantic for main function to hitext([...plugins], printerType, printerSet)
  • Changed first argument of hitext.use() method to be a decorator (i.e. { name, ranges: function(source, createRange) | Array.<range>, printer: Object }) or array of ranges
  • Added generateRanges() method to pipeline
  • Removed first argument (type) in createRange() handler, range's type is now set up automatically
  • Removed spotlight generator (use array of ranges instead)
  • Removed any range specific from build-in printers
  • Added ability to extend printer via a function (createHook method is used), see tty printer examples
  • Removed theme folder that had contained CSS for html printer

1.0.0-alpha.2 (July 10, 2018)

  • Added posibility to create a preset with use() and printer() chaining
  • Removed hitext.generator.lang and related
  • Added hitext.generator.match generator
  • Moved styles from HTML printer aside (to theme/default.css). Styles should be used outside of hitext scope
  • Fixed edge cases in printing (when some ranges have wrong values for start and/or end fields)
  • Added fork() method to all printers

1.0.0-alpha.1 (June 30, 2018)

  • Initial release