@peaceroad/markdown-it-numbering-ul-regarded-as-ol
v0.4.2
Published
This markdown-it plugin regard ul element with numbering lists as ol element.
Downloads
362
Readme
p7d-markdown-it-numbering-ul-regarded-as-ol
Markdown's default ordered list markers are limited. This plugin extends Markdown's unordered lists so they can use many kinds of ordered markers.
Also, this plugin option provides a description-list conversion. Unordered list items whose first line is wrapped in ** and followed by a description separator (two spaces, \\, or a blank line) are converted into <dl> elements.
Ordered lists conversion behavior
This plugin detects unordered lists that use number-like markers (for example - a., - ①) and converts them into appropriate ordered lists (<ol>). During conversion the plugin also attaches informative attributes to the generated elements.
Code. Conversion of a lowercase Roman numeral list.
[Markdown]
- i. First item
- ii. Second item
- iii. Third item
[HTML]
<ol type="i" class="ol-lower-roman" data-marker-suffix=".">
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>Supported Marker Types
The plugin supports the following marker types.
- HTML Standard Markers (with
typeattribute)- decimal:
1,2,3, pattern: common - lower-latin:
a,b,c, pattern: common - upper-latin:
A,B,C, pattern: common - lower-roman:
i,ii,iii, pattern: common - upper-roman:
I,II,III, pattern: common
- decimal:
- Custom Markers (with
role="list")- circled-decimal:
①,②,③, pattern: enclosed - filled-circled-decimal:
❶,❷,❸, pattern: enclosed - circled-lower-latin:
ⓐ,ⓑ,ⓒ, pattern: enclosed - circled-upper-latin:
Ⓐ,Ⓑ,Ⓒ, pattern: enclosed - filled-circled-upper-latin:
🅐,🅑,🅒, pattern: enclosed - squared-decimal:
1⃣,2⃣,3⃣, pattern: enclosed - squared-upper-latin:
🄰,🄱,🄲, pattern: enclosed - filled-squared-upper-latin:
🅰,🅱,🅲, pattern: enclosed - fullwidth-lower-roman:
ⅰ,ⅱ,ⅲ, pattern: fullwidth - fullwidth-upper-roman:
Ⅰ,Ⅱ,Ⅲ, pattern: fullwidth - fullwidth-decimal:
0,1,2, pattern: fullwidth - japanese-informal:
一,二,三, pattern: fullwidth - katakana:
ア,イ,ウ, pattern: fullwidth - katakana-iroha:
イ,ロ,ハ, pattern: fullwidth - lower-greek:
α,β,γ, pattern: common
- circled-decimal:
Marker separators differ depending on the character style used for the ordinal marker: ASCII markers (- a., - a), (a)), fullwidth markers (- 一、, - 一.), or enclosed glyphs (- ①, - ❶). The pattern value for each marker type indicates which separator rules apply:
common: examples includea.,a),(a);fullwidth: includes fullwidth separators such asイ.,イ),(イ);enclosed: enclosed glyphs like①which do not use.or)as separators.
After the marker separator, an ASCII space is normally expected. For fullwidth and enclosed patterns a fullwidth space is accepted in place of the ASCII space; enclosed markers may also appear without a following space. See listTypes.json for the canonical marker definitions.
Attributes and markers
- The generated
<ol>receives aclassof the formol-<marker-name>. For example, a- a.marker results inclass="ol-lower-latin"on the<ol>. - Standard HTML markers (decimal, latin, roman) produce an
<ol>with atypeattribute (for exampletype="1",type="a",type="i"). Custom markers (circled numbers, Katakana, Kanji, etc.) do not set atypeattribute by default; instead the plugin emitsrole="list"on the<ol>. - When the starting number is not
1, the plugin adds a nativestartattribute to the<ol>for both standard and custom markers. - When a marker separator is present, the plugin adds
data-marker-prefixand/ordata-marker-suffixattributes with the matched strings. These attributes are omitted if there is no prefix/suffix (or if the suffix is visually only whitespace). For example,- a.results indata-marker-suffix=".". - For custom markers the plugin inserts the visible marker text inside a
spanwith the configuredmarkerSpanClass(default:li-num). Example:- ①becomes<li><span class="li-num" aria-hidden="true">①</span> A item.</li>. - When individual list item numbers jump, the plugin sets the native
valueattribute on thelielement for both standard and custom markers.
Structures
- Marker type detection is deterministic: gather all markers at a nesting level, match them against the canonical definitions, keep the type that explains the most items while preserving numeric continuity, and use the order in
listTypes.jsononly as a final tiebreaker. - Flattening: A pattern like
- 1.is represented by the defaultul > li > olnesting structure in markdown-it, but this plugin simplifies it to a singleolby default to match the representation of other markers. - With
markdown-it-attrs, attr blocks follow that plugin's nearest-block behavior. This plugin does not reassign list attrs after flattening.
Source map behavior
The flattener uses token.map (markdown-it source line numbers) to preserve loose list output for - 1. style lists. markdown-it normally assigns map to block tokens, but it can be missing if tokens are constructed manually, cloned without map, or stripped by upstream plugins to save memory. In that case, the plugin falls back to markdown-it's paragraph.hidden flags and - 1. lists with blank lines may render as tight lists (no <p> wrappers). Avoid stripping map data or set unremoveUlNest: true if exact loose rendering is required.
Note: For custom marker lists (those rendered with role="list") the plugin assumes you will hide the native marker via CSS (for example ol[role="list"] { list-style: none; }). The hasListStyleNone option can be enabled to add style="list-style: none;" directly to generated <ol> elements.
Behavior customization
You can customize the conversion using options.
unremoveUlNest(boolean) — Iftrue, preserve the originalul > li > olnesting instead of flattening intool > li.alwaysMarkerSpan(boolean) — Wrap markers in a<span>(classmarkerSpanClass, default:li-num). When markers are rendered as custom markers the plugin emitsrole="list"and does not set atypeattribute on the<ol>.useCounterStyle(boolean) — Whentruethe plugin suppresses generated marker spans (nospan.li-num) and prefers nativestart/valueattributes so you can style lists with CSS@counter-style. Note that in this case the marker will not be selectable, as browsers currently do not support CSSuser-selecton markers.markerSpanClass(string) — Specify the class name applied to the marker<span>(default:li-num).hasListStyleNone(boolean) — When the plugin emitsrole="list", also addstyle="list-style: none;"to the<ol>.omitMarkerMetadata(boolean) — Iftrue, omit thedata-marker-prefix/data-marker-suffixattributes.addMarkerStyleToClass(boolean) — Whentrue, append suffix-style information to the generated class name (e.g.ol-decimal-with-round-round). Whenfalse(default) the class stays asol-decimal.enableLiteralNumberingFix(boolean) — Enable literal nested list recovery (for example, nested lists starting with 2 or greater). Default isfalse(legacy-compatible); set it totrueto normalize literal nested numbering. It only applies inside list items, evaluates indentation relative to the parent list marker (marker width + 0–3 spaces), and does not convert code blocks (indent >= marker width + 4).- Compatibility note: compared with the default/legacy mode (
enableLiteralNumberingFix: false), enablingtruechanges rendered HTML by design. On the current test corpus (399markdown cases),19cases change. - Changed files in that comparison:
examples-default-14-repeated-numbers.txt(7),examples-option-literal-numbering-attrs-disabled.txt(1),examples-option-literal-numbering-attrs.txt(1),examples-option-literal-numbering-fix-disabled.txt(2),examples-option-literal-numbering-fix.txt(4),examples-option-literal-numbering-indent.txt(4). - Migration tip: if you enable
enableLiteralNumberingFix: true, treat it as a breaking-output change for snapshots/downstream HTML. - Detailed notes:
docs/enable-literal-numbering-fix.md.
- Compatibility note: compared with the default/legacy mode (
Description lists conversion behavior
When descriptionList or descriptionListWithDiv is enabled, the plugin converts specially formatted bullet lists into HTML description lists (<dl>).
- Each list item must start with a
**Term**line.- The
**Term**line must be the first direct block inside that list item (term-like text inside nested sub-lists does not trigger conversion). - If the description continues on the next line without a blank line, the Term line must end with two ASCII spaces (a Markdown line-break) or a backslash
\. Inline{.attrs}immediately after the term are allowed. - If the Term line is followed by a blank line, the description can start in the next paragraph or list (line-break control characters are optional).
- Same-line descriptions such as
- **Term** Descriptionare not converted. - Same-line backslash forms such as
- **Term**\ Descriptionare also not converted (the hard break marker must be followed by an actual newline). - Term-line attrs can be written in multiple adjacent blocks (
**Term** {.a} {#id} {data-x=1}) and are merged onto<dt>. - Attr-like braces are kept as description text only when your plugin chain does not parse them as attributes. With
markdown-it-attrs, forms like{foo}may be treated as boolean attributes.
- The
In the conversion the **Term** line becomes a <dt> and the subsequent lines become the corresponding <dd>.
Note: Text descriptions are wrapped in <p> elements; additional paragraphs and lists keep markdown-it's block structure.
Description list options
descriptionList(boolean) — Enable conversion of**Term**list patterns into<dl>description lists.descriptionListWithDiv(boolean) — Wrap<dt>/<dd>pairs in a<div>when enabled. This option also enables description-list conversion even whendescriptionListisfalse.descriptionListDivClass(string) — Class applied to the wrapper<div>whendescriptionListWithDivis enabled (empty string disables the class).
Examples: Ordered Lists
HTML standard marker conversion (markers that set the type attribute). The plugin also accepts ) as a separator in place of .:
[Markdown]
- A) First
- B) Second
- C) Third
[HTML]
<ol type="A" class="ol-upper-latin" data-marker-suffix=")">
<li>First</li>
<li>Second</li>
<li>Third</li>
</ol>Custom marker conversion example:
[Markdown]
- ① First
- ② Second
- ③ Third
[HTML]
<ol role="list" class="ol-circled-decimal">
<li><span class="li-num" aria-hidden="true">①</span> First</li>
<li><span class="li-num" aria-hidden="true">②</span> Second</li>
<li><span class="li-num" aria-hidden="true">③</span> Third</li>
</ol>
<!-- In this case the stylesheet is expected to hide the native marker, e.g. ol[role="list"] { list-style: none; } -->Standard numeric markdown is converted as usual:
[Markdown]
1. First item
2. Second item
[HTML]
<ol type="1" class="ol-decimal" data-marker-suffix=".">
<li>First item</li>
<li>Second item</li>
</ol>For lists written in the - 1. style this plugin flattens the default ul > li > ol nesting into a single ol > li structure to match other conversion patterns:
[Markdown]
- 1. First item
- 2. Second item
- 3. Third item
[HTML]
<ol type="1" class="ol-decimal" data-marker-suffix=".">
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>Nested lists are supported as well:
[Markdown]
- ② First item
- i. Subitem A
- iii. Subitem C
- ④ Second item
[HTML]
<ol role="list" start="2" class="ol-circled-decimal">
<li><span class="li-num" aria-hidden="true">②</span> First item
<ol type="i" class="ol-lower-roman" data-marker-suffix=".">
<li>Subitem A</li>
<li value="3">Subitem C</li>
</ol>
</li>
<li value="4"><span class="li-num" aria-hidden="true">④</span> Second item</li>
</ol>
[Markdown]
- 1. Parent item
- a. Child item A
- b. Child item B
- 2. Another parent
[HTML]
<ol type="1" class="ol-decimal" data-marker-suffix=".">
<li>Parent item
<ol type="a" class="ol-lower-latin" data-marker-suffix=".">
<li>Child item A</li>
<li>Child item B</li>
</ol>
</li>
<li>Another parent</li>
</ol>The plugin also handles loose lists (lists where items are separated by blank lines):
[Markdown]
- a. First item
- b. Second item
[HTML]
<ol type="a" class="ol-lower-latin" data-marker-suffix=".">
<li>
<p>First item</p>
</li>
<li>
<p>Second item</p>
</li>
</ol>
[Markdown]
- a. First item first paragraph.
First item second paragraph.
- b. Second item first paragraph.
Second item second paragraph.
[HTML]
<ol type="a" class="ol-lower-latin" data-marker-suffix=".">
<li>
<p>First item first paragraph.</p>
<p>First item second paragraph.</p>
</li>
<li>
<p>Second item first paragraph.</p>
<p>Second item second paragraph.</p>
</li>
</ol>Examples: Description Lists
When description lists are enabled the plugin can convert the following patterns:
[Markdown]
- **Term 1**
Description text for term 1
- **Term 2**
Description text for term 2
[HTML]
<dl>
<dt>Term 1</dt>
<dd>
<p>Description text for term 1</p>
</dd>
<dt>Term 2</dt>
<dd>
<p>Description text for term 2</p>
</dd>
</dl>
[Markdown]
- **Term 1**\
Description text for term 1
- **Term 2**\
Description text for term 2
[HTML]
<dl>
<dt>Term 1</dt>
<dd>
<p>Description text for term 1</p>
</dd>
<dt>Term 2</dt>
<dd>
<p>Description text for term 2</p>
</dd>
</dl>
[Markdown]
- **Term 1**
Description 1, line 1.
Description 1, line 2.
- **Term 2**
Description 2, line 1.
Description 2, line 2.
[HTML]
<dl>
<dt>Term 1</dt>
<dd>
<p>Description 1, line 1.</p>
<p>Description 1, line 2.</p>
</dd>
<dt>Term 2</dt>
<dd>
<p>Description 2, line 1.</p>
<p>Description 2, line 2.</p>
</dd>
</dl>Install
npm install @peaceroad/markdown-it-numbering-ul-regarded-as-olBasic usage
import mdit from 'markdown-it'
import mditNumberingUl from '@peaceroad/markdown-it-numbering-ul-regarded-as-ol'
const md = new mdit()
md.use(mditNumberingUl)
const html = md.render(`- a. First\n- b. Second`)
console.log(html)Register this plugin only once per markdown-it instance. A second registration fails fast because running the conversion pipeline twice on the same token stream would corrupt already-converted lists.
