npm package: @peaceroad/markdown-it-numbering-ul-regarded-as-ol
Description: This markdown-it plugin regard ul element with numbering lists as ol element.
Author: peaceroad
Latest version: 0.4.1

# 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 `type` attribute)**
    - 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
- **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
    - japanese-informal: `一`, `二`, `三`, pattern: fullwidth
    - katakana: `ア`, `イ`, `ウ`, pattern: fullwidth
    - katakana-iroha: `イ`, `ロ`, `ハ`, pattern: fullwidth
    - lower-greek: `α`, `β`, `γ`, pattern: common

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 include `a.`, `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 a `class` of the form `ol-<marker-name>`. For example, a `- a.` marker results in `class="ol-lower-latin"` on the `<ol>`.
- Standard HTML markers (decimal, latin, roman) produce an `<ol>` with a `type` attribute (for example `type="1"`, `type="a"`, `type="i"`). Custom markers (circled numbers, Katakana, Kanji, etc.) do not set a `type` attribute by default; instead the plugin emits `role="list"` on the `<ol>`.
- When the starting number is not `1`, the plugin adds a native `start` attribute to the `<ol>` for both standard and custom markers.
- When a marker separator is present, the plugin adds `data-marker-prefix` and/or `data-marker-suffix` attributes 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 in `data-marker-suffix="."`.
- For custom markers the plugin inserts the visible marker text inside a `span` with the configured `markerSpanClass` (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 `value` attribute on the `li` element 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.json` only as a final tiebreaker.
- Flattening: A pattern like `- 1.` is represented by the default `ul > li > ol` nesting structure in markdown-it, but this plugin simplifies it to a single `ol` by 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) — If `true`, preserve the original `ul > li > ol` nesting instead of flattening into `ol > li`.
- `alwaysMarkerSpan` (boolean) — Wrap markers in a `<span>` (class `markerSpanClass`, default: `li-num`). When markers are rendered as custom markers the plugin emits `role="list"` and does not set a `type` attribute on the `<ol>`.
- `useCounterStyle` (boolean) — When `true` the plugin suppresses generated marker spans (no `span.li-num`) and prefers native `start`/`value` attributes 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 CSS `user-select` on markers.
- `markerSpanClass` (string) — Specify the class name applied to the marker `<span>` (default: `li-num`).
- `hasListStyleNone` (boolean) — When the plugin emits `role="list"`, also add `style="list-style: none;"` to the `<ol>`.
- `omitMarkerMetadata` (boolean) — If `true`, omit the `data-marker-prefix` / `data-marker-suffix` attributes.
- `addMarkerStyleToClass` (boolean) — When `true`, append suffix-style information to the generated class name (e.g. `ol-decimal-with-round-round`). When `false` (default) the class stays as `ol-decimal`.
- `enableLiteralNumberingFix` (boolean) — Enable literal nested list recovery (for example, nested lists starting with 2 or greater). Default is `false` (legacy-compatible); set it to `true` to 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`), enabling `true` changes rendered HTML by design. On the current test corpus (`395` markdown cases), `19` cases 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`.

## 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** Description` are not converted.
  - Same-line backslash forms such as `- **Term**\ Description` are 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.

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 when `descriptionList` is `false`.
- `descriptionListDivClass` (string) — Class applied to the wrapper `<div>` when `descriptionListWithDiv` is 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-filled-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

```pwsh
npm install @peaceroad/markdown-it-numbering-ul-regarded-as-ol
```

## Basic usage

```js
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)
```