• Stars
    star
    614
  • Rank 70,228 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Syntax highlighting, like GitHub

Close up of The Starry Night by Vincent van Gogh (1889)
with examples of starry-night over it


starry-night

Build Coverage Downloads

Syntax highlighting, like what GitHub uses to highlight code, but free and open source and JavaScript!

Contents

What is this?

This package is an open source version of GitHub’s closed-source PrettyLights project (more on that later). It supports 580+ grammars and its extremely high quality. It uses TextMate grammars which are also used in popular editors (SublimeText, Atom, VS Code, &c). They’re heavy but high quality.

When should I use this?

starry-night is a high quality highlighter (when your readers or authors are programmers, you want this!) that can support tons of grammars (from new things like Astro to much more!) which approaches how GitHub renders code.

It has a WASM dependency, and rather big grammars, which means that starry-night might be too heavy particularly in browsers, in which case lowlight or refractor might be more suitable.

This project is similar to the excellent shiki, and it uses the same underlying dependencies, but starry-night is meant to match GitHub in that it produces classes and works with the CSS it ships, making it easier to add dark mode and other themes with CSS compared to inline styles.

Finally, this package produces objects (an AST), which makes it useful when you want to perform syntax highlighting in a place where serialized HTML wouldn’t work or wouldn’t work well. For example, when you want to show code in a CLI by rendering to ANSI sequences, when you’re using virtual DOM frameworks (such as React or Preact) so that diffing can be performant, or when you’re working with hast or rehype.

Bundled, minified, and gzipped, starry-night and the WASM binary are 185 kB. There are two lists of grammars you can use: common (±35 languages, good for your own site) adds 250 kB and all (~600 languages, useful if you are making a site like GitHub) is 1.6 MB. You can also manually choose which grammars to include (or add to common): a language is typically between 3 and 5 kB. To illustrate, Astro costs 2.1 kB and TSX costs 25.4 kB.

What is PrettyLights?

PrettyLights is the syntax highlighter that GitHub uses to turn this:

```markdown
# Hello, world!
```

…into this:

<span class="pl-mh"><span class="pl-mh">#</span><span class="pl-mh"> </span>Hello, world!</span>

…which is what starry-night does too (some small differences in markup, but essentially the same)!

PrettyLights is responsible for taking the flag markdown, looking it up in languages.yml from github-linguist to figure out that that means markdown, taking a corresponding grammar (in this case atom/language-gfm), doing some GPL magic in C, and turning it into spans with classes.

GitHub is using PrettyLights since December 2014, when it replaced Pygments. They wanted to open source it, but were unable due to licensing issues. Recently (Feb 2019?), GitHub has slowly started to move towards TreeLights, which is based on TreeSitter, and also closed source. If TreeLights includes a language (currently: CSS, CodeQL, EJS, Elixir, Go, HTML, JS, PHP, Python, Ruby, TS), that’ll be used, for everything else PrettyLights is used.

starry-night does what PrettyLights does, not what TreeLights does. I’m hopeful that that will be open sourced in the future and we can mimic both.


Install

This package is ESM only. In Node.js (version 14.14+), install with npm:

npm install @wooorm/starry-night

In Deno with esm.sh:

import {common, createStarryNight} from 'https://esm.sh/@wooorm/starry-night@2'

In browsers with esm.sh:

<script type="module">
  import {common, createStarryNight} from 'https://esm.sh/@wooorm/starry-night@2?bundle'
</script>

To get the CSS in browsers, do (see CSS for more info):

<!-- This supports light and dark mode automatically. -->
<link rel="stylesheet" href="https://esm.sh/@wooorm/starry-night@2/style/both.css">

Use

import {common, createStarryNight} from '@wooorm/starry-night'

const starryNight = await createStarryNight(common)

const scope = starryNight.flagToScope('markdown')
const tree = starryNight.highlight('# hi', scope)

console.log(tree)

Yields:

{
  type: 'root',
  children: [
    {
      type: 'element',
      tagName: 'span',
      properties: {className: ['pl-mh']},
      children: [
        {type: 'text', value: '# '},
        {
          type: 'element',
          tagName: 'span',
          properties: {className: ['pl-en']},
          children: [{type: 'text', value: 'hi'}]
        }
      ]
    }
  ]
}

API

This package exports the identifiers all, common, and createStarryNight. There is no default export.

all

List of all grammars (Array<Grammar>)

common

List of ±35 common grammars (Array<Grammar>)

createStarryNight(grammars[, options])

Create a StarryNight that can highlight things based on the given grammars. This is async to facilitate async loading and registering, which is currently only used for WASM.

Parameters
  • grammars (Array<Grammar>) — grammars to support
  • options (Options, default: {}) — configuration (optional)
Returns

Promise that resolves to an instance which highlights based on the bound grammars (Promise<StarryNight>).

starryNight.flagToScope(flag)

Get the grammar scope (such as text.md) associated with a grammar name (such as markdown or pandoc) or grammar extension (such as .mdwn or .rmd).

This function is designed to accept the first word (when splitting on spaces and tabs) that is used after the opening of a fenced code block:

```js
console.log(1)
```

To match GitHub, this also accepts entire paths:

```path/to/example.js
console.log(1)
```

Note: languages can use the same extensions. For example, .h is reused by many languages. In those cases, you will get one scope back, but it might not be the most popular language associated with an extension. For example, .md is registeded by a Lisp-like language instead of markdown. 🤷‍♂️

Parameters
  • flag (string) — grammar name (such as 'markdown' or 'pandoc'), grammar extension (such as '.mdwn' or '.rmd'), or entire file path ending in extension
Returns

Grammar scope, such as 'text.md' (string or undefined).

Example
import {common, createStarryNight} from '@wooorm/starry-night'

const starryNight = await createStarryNight(common)

console.log(starryNight.flagToScope('pandoc')) // `'text.md'`
console.log(starryNight.flagToScope('workbook')) // `'text.md'`
console.log(starryNight.flagToScope('.workbook')) // `'text.md'`
console.log(starryNight.flagToScope('whatever')) // `undefined`

starryNight.highlight(value, scope)

Highlight programming code.

Parameters
  • value (string) — code to highlight
  • scope (string) — registered grammar scope to highlight as (such as 'text.md')
Returns

Node representing highlighted code (Root).

Example
import {createStarryNight} from '@wooorm/starry-night'
import sourceCss from '@wooorm/starry-night/lang/source.css.js'

const starryNight = await createStarryNight([sourceCss])

console.log(starryNight.highlight('em { color: red }', 'source.css'))

Yields:

{
  type: 'root',
  children: [
    {type: 'element', tagName: 'span', properties: [Object], children: [Array]},
    {type: 'text', value: ' { '},
    {type: 'element', tagName: 'span', properties: [Object], children: [Array]},
    {type: 'text', value: ': '},
    {type: 'element', tagName: 'span', properties: [Object], children: [Array]},
    {type: 'text', value: ' }'}
  ]
}

starryNight.missingScopes()

List scopes that are needed by the registered grammars but that are missing.

To illustrate, the text.xml.svg grammar needs the text.xml grammar. When you register text.xml.svg without text.xml, it will be listed here.

Returns

List of grammar scopes, such as 'text.md' (Array<string>).

Example
import {createStarryNight} from '@wooorm/starry-night'
import textXml from '@wooorm/starry-night/lang/text.xml.js'
import textXmlSvg from '@wooorm/starry-night/lang/text.xml.svg.js'

const svg = await createStarryNight([textXmlSvg])
console.log(svg.missingScopes()) //=> ['text.xml']

const svgAndXml = await createStarryNight([textXmlSvg, textXml])
console.log(svgAndXml.missingScopes()) //=> []

starryNight.register(grammars)

Add more grammars.

Parameters
  • grammars (Array<Grammar>) — grammars to support
Returns

Promise resolving to nothing (Promise<undefined>).

Example
import {createStarryNight} from '@wooorm/starry-night'
import sourceCss from '@wooorm/starry-night/lang/source.css.js'
import textMd from '@wooorm/starry-night/lang/text.md.js'
import {toHtml} from 'hast-util-to-html'

const markdown = '```css\nem { color: red }\n```'

const starryNight = await createStarryNight([textMd])

console.log(toHtml(starryNight.highlight(markdown, 'text.md')))

await starryNight.register([sourceCss])

console.log(toHtml(starryNight.highlight(markdown, 'text.md')))

Yields:

<span class="pl-s">```</span><span class="pl-en">css</span>
<span class="pl-c1">em { color: red }</span>
<span class="pl-s">```</span>
<span class="pl-s">```</span><span class="pl-en">css</span>
<span class="pl-ent">em</span> { <span class="pl-c1">color</span>: <span class="pl-c1">red</span> }
<span class="pl-s">```</span>

starryNight.scopes()

List all registered scopes.

Returns

List of grammar scopes, such as 'text.md' (Array<string>).

Example
import {common, createStarryNight} from '@wooorm/starry-night'

const starryNight = await createStarryNight(common)

console.log(starryNight.scopes())

Yields:

[
  'source.c',
  'source.c++',
  // …
  'text.xml',
  'text.xml.svg'
]

GetOnigurumaUrl

Function to get a URL to the oniguruma WASM (TypeScript type).

👉 Note: this must currently result in a version 1 URL of onig.wasm from vscode-oniguruma.

⚠️ Danger: when you use this functionality, your project might break at any time (when reinstalling dependencies), except when you make sure that the WASM binary you load manually is what our internally used vscode-oniguruma dependency expects. To solve this, you could for example use an npm script called dependencies (which runs everytime node_modules is changed) which copies vscode-oniguruma/release/onig.wasm to the place you want to host it.

Returns

URL object to a WASM binary (URL or Promise<URL>).

Example
import {common, createStarryNight} from '@wooorm/starry-night'

const starryNight = await createStarryNight(common, {
  getOnigurumaUrlFetch() {
    return new URL('/onig.wasm', window.location.href);
  }
})

Grammar

TextMate grammar with some extra info (TypeScript type).

Fields
  • scopeName (string, example: 'source.mdx') — scope
  • names (Array<string>, example: ['mdx']) — list of names
  • dependencies (Array<string> | undefined, example: ['source.tsx']) — list of scopes that are needed for this grammar to work
  • extensions (Array<string>, example: ['.mdx']) — list of extensions
  • extensionsWithDot (Array<string> | undefined, example: []) — list of extensions that only match if used w/ a dot
  • patterns (unknown) — TextMate patterns
  • repository (unknown) — TextMate repository
  • injections (unknown) — TextMate injections

Options

Configuration (TypeScript type).

Fields
  • getOnigurumaUrlFetch (GetOnigurumaUrl, optional) — get a URL to the oniguruma WASM, typically used in browsers
  • getOnigurumaUrlFs (GetOnigurumaUrl, optional) — get a URL to the oniguruma WASM, typically used in Node.js

Root

Root node (TypeScript type).

type
export type {Root} from 'hast'

Examples

Example: serializing hast as html

hast trees as returned by starry-night can be serialized with hast-util-to-html:

import {common, createStarryNight} from '@wooorm/starry-night'
import {toHtml} from 'hast-util-to-html'

const starryNight = await createStarryNight(common)

const tree = starryNight.highlight('"use strict";', 'source.js')

console.log(toHtml(tree))

Yields:

<span class="pl-s"><span class="pl-pds">"</span>use strict<span class="pl-pds">"</span></span>;

Example: using starry-night on the client

You don’t have to do preprocess things on a server. Particularly, when you are not using Node.js or so. Or, when you have a lot of often changing content (likely markdown), such as on a page of comments.

In those cases, you can run starry-night in the browser. Here is an example. It also uses hast-util-to-dom, which is a light way to turn the AST into DOM nodes.

Say we have this example.js on our browser (no bundling needed!):

import {common, createStarryNight} from 'https://esm.sh/@wooorm/starry-night@2?bundle'
import {toDom} from 'https://esm.sh/hast-util-to-dom@3?bundle'

const starryNight = await createStarryNight(common)
const prefix = 'language-'

const nodes = Array.from(document.body.querySelectorAll('code'))

for (const node of nodes) {
  const className = Array.from(node.classList).find((d) => d.startsWith(prefix))
  if (!className) continue
  const scope = starryNight.flagToScope(className.slice(prefix.length))
  if (!scope) continue
  const tree = starryNight.highlight(node.textContent, scope)
  node.replaceChildren(toDom(tree, {fragment: true}))
}

…and then, if we would have an index.html for our document:

<!doctype html>
<meta charset=utf8>
<title>Hello</title>
<link rel=stylesheet href=https://esm.sh/@wooorm/starry-night@2/style/both.css>
<body>
<h1>Hello</h1>
<p>…world!</p>
<pre><code class=language-js>console.log('it works!')
</code></pre>
<script type=module src=./example.js></script>
</body>

Opening that page in a browser, we’d see the <code> being swapped with:

<code class="language-js"><span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>it works!<span class="pl-pds">'</span></span>)
</code>

Example: turning hast into react nodes

hast trees as returned by starry-night can be turned into preact, react, solid, svelte, vue, etc., with hast-util-to-jsx-runtime:

import {common, createStarryNight} from '@wooorm/starry-night'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'react/jsx-runtime'

const starryNight = await createStarryNight(common)

const tree = starryNight.highlight('"use strict";', 'source.js')
const reactNode = toJsxRuntime(tree, {Fragment, jsx, jsxs})

console.log(reactNode)

Yields:

{
  '$$typeof': Symbol(react.element),
  type: Symbol(react.fragment),
  key: null,
  ref: null,
  props: { children: [ [Object], ';' ] },
  _owner: null,
  _store: {}
}

Example: adding line numbers

GitHub itself does not add line numbers to the code they highlight. You can do that, by transforming the AST. Here’s an example of a utility that wraps each line into a span with a class and a data attribute with its line number. That way, you can style the lines as you please. Or you can generate different elements for each line, of course.

Say we have our utility as hast-util-starry-night-gutter.js:

/**
 * @typedef {import('hast').Element} Element
 * @typedef {import('hast').ElementContent} ElementContent
 * @typedef {import('hast').Root} Root
 * @typedef {import('hast').RootContent} RootContent
 */

/**
 * @template {Root} Tree
 * @param {Tree} tree
 * @returns {Tree}
 */
export function starryNightGutter(tree) {
  /** @type {Array<RootContent>} */
  const replacement = []
  const search = /\r?\n|\r/g
  let index = -1
  let start = 0
  let startTextRemainder = ''
  let lineNumber = 0

  while (++index < tree.children.length) {
    const child = tree.children[index]

    if (child.type === 'text') {
      let textStart = 0
      let match = search.exec(child.value)

      while (match) {
        // Nodes in this line.
        const line = /** @type {Array<ElementContent>} */ (
          tree.children.slice(start, index)
        )

        // Prepend text from a partial matched earlier text.
        if (startTextRemainder) {
          line.unshift({type: 'text', value: startTextRemainder})
          startTextRemainder = ''
        }

        // Append text from this text.
        if (match.index > textStart) {
          line.push({
            type: 'text',
            value: child.value.slice(textStart, match.index)
          })
        }

        // Add a line, and the eol.
        lineNumber += 1
        replacement.push(createLine(line, lineNumber), {
          type: 'text',
          value: match[0]
        })

        start = index + 1
        textStart = match.index + match[0].length
        match = search.exec(child.value)
      }

      // If we matched, make sure to not drop the text after the last line ending.
      if (start === index + 1) {
        startTextRemainder = child.value.slice(textStart)
      }
    }
  }

  const line = /** @type {Array<ElementContent>} */ (tree.children.slice(start))
  // Prepend text from a partial matched earlier text.
  if (startTextRemainder) {
    line.unshift({type: 'text', value: startTextRemainder})
    startTextRemainder = ''
  }

  if (line.length > 0) {
    lineNumber += 1
    replacement.push(createLine(line, lineNumber))
  }

  // Replace children with new array.
  tree.children = replacement

  return tree
}

/**
 * @param {Array<ElementContent>} children
 * @param {number} line
 * @returns {Element}
 */
function createLine(children, line) {
  return {
    type: 'element',
    tagName: 'span',
    properties: {className: 'line', dataLineNumber: line},
    children
  }
}

…and a module example.js:

import {common, createStarryNight} from '@wooorm/starry-night'
import {toHtml} from 'hast-util-to-html'
import {starryNightGutter} from './hast-util-starry-night-gutter.js'

const starryNight = await createStarryNight(common)

const tree = starryNight.highlight(
  '# Some heading\n\n```js\nalert(1)\n```\n***',
  'text.md'
)

starryNightGutter(tree)

console.log(toHtml(tree))

Now running node example.js yields:

<span class="line" data-line-number="1"><span class="pl-mh"># <span class="pl-en">Some heading</span></span></span>
<span class="line" data-line-number="2"></span>
<span class="line" data-line-number="3"><span class="pl-s">```</span><span class="pl-en">js</span></span>
<span class="line" data-line-number="4"><span class="pl-en">alert</span>(<span class="pl-c1">1</span>)</span>
<span class="line" data-line-number="5"><span class="pl-s">```</span></span>
<span class="line" data-line-number="6"><span class="pl-ms">***</span></span>

Example: integrate with unified, remark, and rehype

This example shows how to combine starry-night with unified: using remark to parse the markdown and transforming it to HTML with rehype. If we have a markdown file example.md:

# Hello

…world!

```js
console.log('it works!')
```

…and a plugin rehype-starry-night.js:

/**
 * @typedef {import('@wooorm/starry-night').Grammar} Grammar
 * @typedef {import('hast').ElementContent} ElementContent
 * @typedef {import('hast').Root} Root
 */

/**
 * @typedef Options
 *   Configuration (optional)
 * @property {Array<Grammar> | null | undefined} [grammars]
 *   Grammars to support (defaults: `common`).
 */

import {common, createStarryNight} from '@wooorm/starry-night'
import {toString} from 'hast-util-to-string'
import {visit} from 'unist-util-visit'

/**
 * Plugin to highlight code with `starry-night`.
 *
 * @type {import('unified').Plugin<[(Options | null | undefined)?], Root>}
 */
export default function rehypeStarryNight(options) {
  const grammars = (options || {}).grammars || common
  const starryNightPromise = createStarryNight(grammars)
  const prefix = 'language-'

  return async function (tree) {
    const starryNight = await starryNightPromise

    visit(tree, 'element', function (node, index, parent) {
      if (!parent || index === null || node.tagName !== 'pre') {
        return
      }

      const head = node.children[0]

      if (
        !head ||
        head.type !== 'element' ||
        head.tagName !== 'code' ||
        !head.properties
      ) {
        return
      }

      const classes = head.properties.className

      if (!Array.isArray(classes)) return

      const language = classes.find(
        (d) => typeof d === 'string' && d.startsWith(prefix)
      )

      if (typeof language !== 'string') return

      const scope = starryNight.flagToScope(language.slice(prefix.length))

      // Maybe warn?
      if (!scope) return

      const fragment = starryNight.highlight(toString(head), scope)
      const children = /** @type {Array<ElementContent>} */ (fragment.children)

      parent.children.splice(index, 1, {
        type: 'element',
        tagName: 'div',
        properties: {
          className: [
            'highlight',
            'highlight-' + scope.replace(/^source\./, '').replace(/\./g, '-')
          ]
        },
        children: [{type: 'element', tagName: 'pre', properties: {}, children}]
      })
    })
  }
}

…and finally a module example.js:

import fs from 'node:fs/promises'
import {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
import rehypeStarryNight from './rehype-starry-night.js'

const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeStarryNight)
  .use(rehypeStringify)
  .process(await fs.readFile('example.md'))

console.log(String(file))

Now running node example.js yields:

<h1>Hello</h1>
<p>…world!</p>
<div class="highlight highlight-js"><pre><span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>it works!<span class="pl-pds">'</span></span>)
</pre></div>

Example: integrating with markdown-it

This example shows how to combine starry-night with markdown-it. If we have a markdown file example.md:

# Hello

…world!

```js
console.log('it works!')
```

…and a module example.js:

import fs from 'node:fs/promises'
import {common, createStarryNight} from '@wooorm/starry-night'
import {toHtml} from 'hast-util-to-html'
import markdownIt from 'markdown-it'

const file = await fs.readFile('example.md')
const starryNight = await createStarryNight(common)

const markdownItInstance = markdownIt({
  highlight(value, lang) {
    const scope = starryNight.flagToScope(lang)

    return toHtml({
      type: 'element',
      tagName: 'pre',
      properties: {
        className: scope
          ? [
              'highlight',
              'highlight-' + scope.replace(/^source\./, '').replace(/\./g, '-')
            ]
          : undefined
      },
      children: scope
        ? starryNight.highlight(value, scope).children
        : [{type: 'text', value}]
    })
  }
})

const html = markdownItInstance.render(String(file))

console.log(html)

Now running node example.js yields:

<h1>Hello</h1>
<p>…world!</p>
<pre class="highlight highlight-js"><span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>it works!<span class="pl-pds">'</span></span>)
</pre>

Syntax tree

The generated hast starts with a root node, that represents the fragment. It contains up to three levels of <span> elements, each with a single class. All these levels can contain text nodes with the actual code. Interestingly, TextMate grammars work per line, so all line endings are in the root directly, meaning that creating a gutter to display line numbers can be generated rather naïvely by only looking through the root node.

CSS

starry-night does not inject CSS for the syntax highlighted code (because well, starry-night doesn’t have to be turned into HTML and might not run in a browser!). If you are in a browser, you can use the packaged themes, or get creative with CSS! 💅

All themes accept CSS variables (custom properties). With the theme core.css, you have to define your own properties. All other themes define the colors on :root. Themes either have a dark or light suffix, or none, in which case they automatically switch colors based on a @media (prefers-color-scheme: dark). All themes are tiny (under 1 kB). The shipped themes are as follows:

name Includes light scheme Includes dark scheme
core.css
light.css
dark.css
both.css
colorblind-light.css
colorblind-dark.css
colorblind.css
dimmed-dark.css
dimmed.css
high-contrast-light.css
high-contrast-dark.css
high-contrast.css
tritanopia-light.css
tritanopia-dark.css
tritanopia.css

Languages

Checked grammars are included in common. Everything (that’s needed) is available through all. You can add more grammars as you please.

Each grammar has several associated names and extensions. See source files for which are known and use flagToScope to turn them into scopes.

Some grammars need other grammars to work. You are responsible for loading those, use missingScopes to find which dependencies are needed.

All licenses are permissive and made available in notice. Changes should go to upstream repos and languages.yml in github-linguist.

Types

This package is fully typed with TypeScript. It exports the additional types GetOnigurumaUrl, Grammar, Options, and Root.

Compatibility

This project is compatible with maintained versions of Node.js.

When we cut a new major release, we drop support for unmaintained versions of Node. This means we try to keep the current release line, wooorm@starry-night@^2, compatible with Node.js 14+.

You can pass your own TextMate grammars, provided that they work with vscode-textmate, and that they have the added fields scopeName, names, and extensions (see types for the definitions and the grammars in lang/ for examples).

Security

This package is safe.

Related

  • lowlight — similar but based on highlight.js
  • refractor — similar but based on Prism

Contribute

Yes please! See How to Contribute to Open Source.

License

The grammars included in this package are covered by their repositories’ respective licenses, which are permissive (apache-2.0, mit, etc), and made available in notice.

All other files MIT © Titus Wormer


More Repositories

1

franc

Natural language detection
JavaScript
3,906
star
2

dictionaries

Hunspell dictionaries in UTF-8
JavaScript
1,051
star
3

markdown-rs

CommonMark compliant markdown parser in Rust with ASTs and extensions
Rust
736
star
4

xdm

Just a *really* good MDX compiler. No runtime. With esbuild, Rollup, and webpack plugins
JavaScript
589
star
5

lowlight

Virtual syntax highlighting for virtual DOMs and non-HTML things
JavaScript
553
star
6

refractor

Lightweight, robust, elegant virtual syntax highlighting using Prism
JavaScript
535
star
7

mdxjs-rs

Compile MDX to JavaScript in Rust
Rust
387
star
8

nspell

📝 Hunspell compatible spell-checker
JavaScript
260
star
9

markdown-table

Generate a markdown (GFM) table
JavaScript
249
star
10

gemoji

Info on gemoji (GitHub Emoji)
JavaScript
218
star
11

write-music

visualise sentence length
JavaScript
192
star
12

readability

visualise readability
JavaScript
185
star
13

parse-english

English (natural language) parser
JavaScript
159
star
14

server-components-mdx-demo

React server components + MDX
JavaScript
123
star
15

emphasize

ANSI syntax highlighting for the terminal
JavaScript
101
star
16

linked-list

Minimalistic linked lists
JavaScript
81
star
17

levenshtein.c

Levenshtein algorithm in C
C
79
star
18

import-meta-resolve

Resolve things like Node.js — ponyfill for `import.meta.resolve`
JavaScript
78
star
19

short-words

visualise lengthy words
JavaScript
65
star
20

trough

`trough` is middleware
JavaScript
61
star
21

bcp-47

Parse and stringify BCP 47 language tags
JavaScript
59
star
22

html-tag-names

List of known HTML tag names
JavaScript
58
star
23

parse-latin

Latin-script (natural language) parser
JavaScript
57
star
24

iso-3166

ISO 3166 (standard for country codes and codes for their subdivisions)
JavaScript
51
star
25

html-element-attributes

Map of HTML elements to allowed attributes
JavaScript
51
star
26

trim-lines

Remove spaces and tabs around line-breaks
JavaScript
50
star
27

common-words

visualise rare words
JavaScript
49
star
28

parse-entities

Parse HTML character references
JavaScript
46
star
29

iso-639-3

Info on ISO 639-3
JavaScript
46
star
30

levenshtein-rs

Levenshtein algorithm in Rust
Rust
42
star
31

emoticon

List of emoticons
JavaScript
40
star
32

direction

Detect directionality: left-to-right, right-to-left, or neutral
JavaScript
39
star
33

textom

DEPRECATED in favour of retext’s virtual object model
39
star
34

dictionary

Dictionary app that can work without JavaScript or internet
JavaScript
37
star
35

f-ck

🤬 Clean-up cuss words
JavaScript
37
star
36

dioscuri

A gemtext (`text/gemini`) parser with support for streaming, ASTs, and CSTs
JavaScript
34
star
37

property-information

Info on the properties and attributes of the web platform
JavaScript
33
star
38

stmr.c

Porter Stemmer algorithm in C
C
32
star
39

eslint-md

Deprecated
30
star
40

svg-tag-names

List of known SVG tag names
JavaScript
29
star
41

checkmoji

Check emoji across platforms
JavaScript
26
star
42

html-void-elements

List of known void HTML elements
JavaScript
26
star
43

npm-high-impact

The high-impact (popular) packages of npm
JavaScript
26
star
44

iso-639-2

Info on ISO 639-2
JavaScript
23
star
45

aria-attributes

List of ARIA attributes
JavaScript
21
star
46

stringify-entities

Serialize (encode) HTML character references
JavaScript
21
star
47

bcp-47-match

Match BCP 47 language tags with language ranges per RFC 4647
JavaScript
19
star
48

speakers

Speaker count for 450+ languages
JavaScript
19
star
49

svg-element-attributes

Map of SVG elements to allowed attributes
JavaScript
19
star
50

osx-learn

Add words to the OS X Spell Check dictionary
Shell
18
star
51

trigrams

Trigram files for 400+ languages
JavaScript
18
star
52

fault

Functional errors with formatted output
JavaScript
17
star
53

remark-preset-wooorm

Personal markdown (and prose) style
JavaScript
17
star
54

udhr

Universal declaration of human rights
HTML
17
star
55

bcp-47-normalize

Normalize, canonicalize, and format BCP 47 tags
JavaScript
16
star
56

happy-places

Little list of happy places
15
star
57

wooorm.github.io

🐛 personal website
JavaScript
14
star
58

plain-text-data-to-json

Transform a simple plain-text database to JSON
JavaScript
14
star
59

parse-dutch

Dutch (natural language) parser
JavaScript
14
star
60

zwitch

Handle values based on a property
JavaScript
13
star
61

match-casing

Match the case of `value` to that of `base`
JavaScript
13
star
62

link-rel

List of valid values for `rel` on `<link>`
JavaScript
13
star
63

npm-esm-vs-cjs

Data on the share of ESM vs CJS on the public npm registry
JavaScript
13
star
64

linter-remark

Check markdown with remark in atom
13
star
65

is-badge

Check if `url` is a badge
JavaScript
13
star
66

vendors

List of vendor prefixes known to the web platform
JavaScript
12
star
67

load-plugin

Load a submodule / plugin
JavaScript
12
star
68

comma-separated-tokens

Parse and stringify comma-separated tokens
JavaScript
11
star
69

bail

Throw if given an error
JavaScript
11
star
70

space-separated-tokens

Parse and stringify space-separated tokens
JavaScript
10
star
71

trigram-utils

A few language trigram utilities
JavaScript
10
star
72

collapse-white-space

Collapse white space.
JavaScript
9
star
73

retext-language

Detect then language of text with Retext
JavaScript
9
star
74

longest-streak

Count the longest repeating streak of a substring
JavaScript
9
star
75

unherit

Clone a constructor without affecting the super-class
JavaScript
9
star
76

markdown-escapes

Legacy: list of escapable characters in markdown
JavaScript
9
star
77

state-toggle

Enter/exit a state
JavaScript
9
star
78

meta-name

List of values that can be used as `name`s on HTML `meta` elements
JavaScript
9
star
79

html-dangerous-encodings

List of dangerous HTML character encoding labels
JavaScript
8
star
80

character-entities

Map of named character references.
JavaScript
8
star
81

stmr

Porter Stemmer CLI
C
8
star
82

levenshtein

Levenshtein algorithm CLI
Shell
8
star
83

commonmark.json

CommonMark test spec in JSON
JavaScript
8
star
84

web-namespaces

Map of web namespaces
JavaScript
7
star
85

is-whitespace-character

Check if a character is a white space character
JavaScript
7
star
86

strip-skin-tone

Strip skin tone modifiers (as in Fitzpatrick scale) from emoji (🎅🏿 to 🎅)
JavaScript
7
star
87

atom-travis

Install Atom on Travis
Shell
7
star
88

svg-event-attributes

List of SVG event handler attributes
JavaScript
7
star
89

control-pictures

Replace pictures for control character codes with actual control characters
JavaScript
7
star
90

css-declarations

Legacy utility to parse and stringify CSS declarations
JavaScript
6
star
91

html-encodings

Info on HTML character encodings.
JavaScript
6
star
92

mathml-tag-names

List of known MathML tag names
JavaScript
6
star
93

array-iterate

`Array#forEach()` but it’s possible to define where to move to next
JavaScript
6
star
94

atom-tap-test-runner

Run Atom package tests using TAP
6
star
95

ccount

Count how often a substring occurs
JavaScript
6
star
96

doctype

Info on HTML / XHTML / MathML / SVG doctypes
JavaScript
6
star
97

retext-english

Moved
6
star
98

labels

GitHub labels
6
star
99

remark-range

Deprecated
6
star
100

dead-or-alive

check if urls are dead or alive
JavaScript
6
star