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 🙏

© 2026 – Pkg Stats / Ryan Hefner

markdown-it-underline-cjk-friendly

v1.0.3

Published

Renders _underline_ to <u>underline</u>

Downloads

30

Readme

markdown-it-underline

Renders _underline_ to <u>underline</u>.
— CJK-friendly, language-agnostic.

npm GitHub License: MIT

Why this plugin?

Standard Markdown offers two ways to produce italic: *emphasis* and _emphasis_. Having two syntaxes for the same output is confusing. This plugin reclaims the underscore syntax to produce underline instead, giving each marker a distinct purpose:

| Input | Output | |---|---| | _underline_ | <u>underline</u> | | *emphasis* | <em>emphasis</em> | | **strong** | <strong>strong</strong> | | __text__ | Not modified by this plugin, Default — <strong>text</strong> |

The __double underscore__ syntax is not modified by this plugin. By default it remains strong emphasis (<strong>), but you are free to install another plugin to customize its behavior.

Why not CommonMark?

This plugin deliberately deviates from CommonMark because CommonMark's flanking rules are unfriendly to CJK (Chinese, Japanese, Korean) and other scripts that do not use spaces between words.

In standard CommonMark, _你好_世界! does not produce underline because the closing _ is considered "intraword." To make it work you would need to write _你好_ 世界! (with an ugly space) or insert <wbr> tags — both of which clutter your source text and harm readability.

This plugin treats underscores the same way across all writing systems:

| Input | Output | |---|---| | 你_好_世界! | 你<u>好</u>世界! | | Hello_w_orld | Hello<u>w</u>orld | | Γεια _σ_ας κόσμος | Γεια <u>σ</u>ας κόσμος |

Installation

# npm
npm install markdown-it-underline-cjk-friendly

# yarn
yarn add markdown-it-underline-cjk-friendly

# pnpm
pnpm add markdown-it-underline-cjk-friendly

Usage

import markdownIt from "markdown-it";
import markdownItUnderline from "markdown-it-underline-cjk-friendly";

const md = markdownIt().use(markdownItUnderline);

md.render("_Hello_ World!");
// <p><u>Hello</u> World!</p>

Features

Basic underline and coexistence with emphasis

_underline_ becomes <u> while *emphasis* and **strong** remain unchanged:

_underline_ *emphasis* **strong**
<p><u>underline</u> <em>emphasis</em> <strong>strong</strong></p>

Nesting

Underline nests correctly with emphasis, strong, and strikethrough:

_*underline emphasis*_
*_underline emphasis_*
**bold *italic _underline ~~strikethrough~~ underline_ italic* bold**
<p><u><em>underline emphasis</em></u>
<em><u>underline emphasis</u></em></p>
<p><strong>bold <em>italic <u>underline <s>strikethrough</s> underline</u> italic</em> bold</strong></p>

Lone underscores are ignored

A single _ with no matching pair is treated as literal text — my_var, _hello, and hello_ all remain unchanged.

Language-agnostic

Works identically across all scripts. A single character between underscores is underlined regardless of language:

| Language | Markdown | HTML | |--|--|--| | English | un_d_erline | un<u>d</u>erline | Greek | υ_π_ογράμμιση | υ<u>π</u>ογράμμιση | | Cyrillic | по_д_черкнуть | по<u>д</u>черкнуть | | Armenian | ը_ն_դգծել | ը<u>ն</u>դգծել | | Georgian | ხა_ზ_გასწორება | ხა<u>ზ</u>გასწორება | | Arabic | ا_ل_تأكيد | ا<u>ل</u>تأكيد | | Devanagari | अं_ड_रलाइन | अं<u>ड</u>रलाइन | | Thai | บรร_ย_าย | บรร<u>ย</u>าย | | Bengali | আন্ডা_র_লাইন | আন্ডা<u>র</u>লাইন | | Burmese | အော_က်_ခြေ | အော<u>က်</u>ခြေ | | Chinese | 下_划_线 | 下<u>划</u>线 | | Japanese | アン_ダ_ーライン | アン<u>ダ</u>ーライン | | Korean | 하_드_라인 | 하<u>드</u>라인 |

CJK text

Underscores work naturally with CJK characters in any position:

_你好_世界!
你_好_世界!
你好_世界_!
你好_世界!_
<p><u>你好</u>世界!</p>
<p>你<u>好</u>世界!</p>
<p>你好<u>世界</u>!</p>
<p>你好<u>世界!</u></p>

Escaping

CAUTION:

_file_name_ will become <u>file</u>name_, which may not you want.
In that case, you need escaping.

Use backslashes to escape underscores:
Useful to keep snake_case variable names.

_file_name_        → <u>file</u>name_
_file\_name_       → <u>file_name</u>
\_file_name_       → _file<u>name</u>
\_file\_name\_     → _file_name_

Mixed marker priority

Whichever marker opens first takes priority:

*foo_bar*baz_     → <em>foo_bar</em>baz_      (* opened first)
_foo*bar_baz*     → <u>foo*bar</u>baz*        (_ opened first)
**foo_bar**baz_   → <strong>foo_bar</strong>baz_
_foo**bar_baz**   → <u>foo**bar</u>baz**
~~foo_bar~~baz_   → <s>foo_bar</s>baz_
_foo~~bar_baz~~   → <u>foo~~bar</u>baz~~

Code spans and code blocks

Underscores inside inline code and fenced code blocks are never treated as underline:

`get_local_documents`
_foo`bar_baz`
<p><code>get_local_documents</code></p>
<p>_foo<code>bar_baz</code></p>

Links and images

Link text supports underline; link URLs, image URLs, and image alt text are left untouched:

[hello_world_again](http://example.com/hello_world_again)
![hello_world_again](http://example.com/hello_world_again.jpg)
<p><a href="http://example.com/hello_world_again">hello<u>world</u>again</a></p>
<p><img src="http://example.com/hello_world_again.jpg" alt="hello_world_again"></p>

Reference links work the same way — underline in link text, not in reference tags, and no crossing between adjacent reference links.

Tables

Underscores do not cross table cell boundaries. Inside a cell, paired underscores produce underline:

| f_o_o | b_a_r |
|-------|-------|
| b_a_z | q_u_x |
<table>
<thead>
<tr><th>f<u>o</u>o</th><th>b<u>a</u>r</th></tr>
</thead>
<tbody>
<tr><td>b<u>a</u>z</td><td>q<u>u</u>x</td></tr>
</tbody>
</table>

Double and triple underscores

__text__ is not handled by this plugin — it remains as <strong>text</strong> (default markdown-it behavior), or can be customized by another plugin.

___text___ is also not handled by this plugin, but by the default behavior, it could be <u><strong>text</strong></u>.

On semantics

Is <u> semantically correct? Maybe not — but who cares?

  • <u> is the simplest way to create underlined text without CSS.
  • <ins> looks identical but implies document insertion, which is semantically worse for general underlining.
  • Repurposing <em> with CSS:
    em {
      font-style: normal;
      text-decoration: underline;
    }
    would break the *italic* syntax.

Sometimes practicality beats purity.

References

License

MIT