markdown-it-dl-list 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README-ja.md +133 -0
- package/README.md +131 -0
- package/dist/index.cjs +273 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +253 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yohei Kanamura
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README-ja.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# markdown-it-dl-list
|
|
2
|
+
|
|
3
|
+
`<dl>`, `<dt>`, `<dd>` を使った
|
|
4
|
+
**コロン記法の定義リスト**をサポートする **markdown-it プラグイン**です。
|
|
5
|
+
|
|
6
|
+
Pandoc などに見られる定義リスト構文に着想を得た、
|
|
7
|
+
シンプルで読みやすい記法を markdown-it で利用できます。
|
|
8
|
+
|
|
9
|
+
## 特徴
|
|
10
|
+
|
|
11
|
+
- コロン(`:`)による定義リスト構文
|
|
12
|
+
- `<dl>`, `<dt>`, `<dd>` を生成
|
|
13
|
+
- 1つの用語に複数の定義を記述可能
|
|
14
|
+
- 定義を持たない用語(dt-only)に対応
|
|
15
|
+
- 入れ子の定義リストをサポート
|
|
16
|
+
- 標準的な markdown-it の処理フローに統合可能
|
|
17
|
+
|
|
18
|
+
## インストール
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install markdown-it-dl-list
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 使い方
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import MarkdownIt from "markdown-it";
|
|
28
|
+
import dlList from "markdown-it-dl-list";
|
|
29
|
+
|
|
30
|
+
const md = new MarkdownIt();
|
|
31
|
+
md.use(dlList);
|
|
32
|
+
|
|
33
|
+
const src = `
|
|
34
|
+
: 用語
|
|
35
|
+
: 説明文その1
|
|
36
|
+
: 説明文その2
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
console.log(md.render(src));
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
出力結果:
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<dl>
|
|
46
|
+
<dt>用語</dt>
|
|
47
|
+
<dd>説明文その1</dd>
|
|
48
|
+
<dd>説明文その2</dd>
|
|
49
|
+
</dl>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 構文
|
|
53
|
+
|
|
54
|
+
### 基本形
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
: 用語
|
|
58
|
+
: 説明文
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 複数の定義
|
|
62
|
+
|
|
63
|
+
```markdown
|
|
64
|
+
: 用語
|
|
65
|
+
: 説明文その1
|
|
66
|
+
: 説明文その2
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 定義を持たない用語(dt-only)
|
|
70
|
+
|
|
71
|
+
定義を持たない用語は、
|
|
72
|
+
**直後が空行またはファイル末尾の場合のみ**有効です。
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
: 用語のみ
|
|
76
|
+
|
|
77
|
+
次の行
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 複数行の用語(dt 継続行)
|
|
81
|
+
|
|
82
|
+
用語行の直後にインデントされた行が続く場合、
|
|
83
|
+
それらは同じ用語として扱われます。
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
: これは複数行の
|
|
87
|
+
用語です。
|
|
88
|
+
: これは複数行の
|
|
89
|
+
説明文です。
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 入れ子の定義リスト
|
|
93
|
+
|
|
94
|
+
```markdown
|
|
95
|
+
: 外側の用語
|
|
96
|
+
: : 内側の用語
|
|
97
|
+
: 内側の説明文
|
|
98
|
+
: 次の説明文
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## オプション
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
type DlListOptions = {
|
|
105
|
+
/** dd 行に必要なインデント(スペース数)。デフォルト: 4 */
|
|
106
|
+
ddIndent?: number;
|
|
107
|
+
|
|
108
|
+
/** 定義(dd)を必須とするかどうか。デフォルト: true */
|
|
109
|
+
requireDd?: boolean;
|
|
110
|
+
|
|
111
|
+
/** 空行で現在の定義リストを終了するか。デフォルト: true */
|
|
112
|
+
breakOnBlankLine?: boolean;
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
使用例:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
md.use(dlList, {
|
|
120
|
+
ddIndent: 2,
|
|
121
|
+
requireDd: true,
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## このプラグインが「しないこと」
|
|
126
|
+
|
|
127
|
+
* 定義リスト以外の Markdown の挙動は変更しません
|
|
128
|
+
* markdown-it の標準的な段落処理は維持されます
|
|
129
|
+
* すべての既存定義リスト構文を網羅することは目的としていません
|
|
130
|
+
|
|
131
|
+
## ライセンス
|
|
132
|
+
|
|
133
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# markdown-it-dl-list
|
|
2
|
+
|
|
3
|
+
A **markdown-it plugin** that adds support for **colon-based definition lists**
|
|
4
|
+
using `<dl>`, `<dt>`, and `<dd>`.
|
|
5
|
+
|
|
6
|
+
This plugin enables a simple and readable definition list syntax inspired by
|
|
7
|
+
Pandoc and other Markdown variants.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Colon-based definition list syntax
|
|
12
|
+
- Supports `<dl>`, `<dt>`, and `<dd>`
|
|
13
|
+
- Multiple definitions per term
|
|
14
|
+
- Term-only entries (dt-only)
|
|
15
|
+
- Nested definition lists
|
|
16
|
+
- Designed to work with standard markdown-it pipelines
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install markdown-it-dl-list
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import MarkdownIt from "markdown-it";
|
|
28
|
+
import dlList from "markdown-it-dl-list";
|
|
29
|
+
|
|
30
|
+
const md = new MarkdownIt();
|
|
31
|
+
md.use(dlList);
|
|
32
|
+
|
|
33
|
+
const src = `
|
|
34
|
+
: Term
|
|
35
|
+
: Definition line 1
|
|
36
|
+
: Definition line 2
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
console.log(md.render(src));
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Output:
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<dl>
|
|
46
|
+
<dt>Term</dt>
|
|
47
|
+
<dd>Definition line 1</dd>
|
|
48
|
+
<dd>Definition line 2</dd>
|
|
49
|
+
</dl>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Syntax
|
|
53
|
+
|
|
54
|
+
### Basic form
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
: Term
|
|
58
|
+
: Definition
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Multiple definitions
|
|
62
|
+
|
|
63
|
+
```markdown
|
|
64
|
+
: Term
|
|
65
|
+
: First definition
|
|
66
|
+
: Second definition
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Term-only (dt-only)
|
|
70
|
+
|
|
71
|
+
A term without definitions is allowed **only when followed by a blank line or EOF**:
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
: Term only
|
|
75
|
+
|
|
76
|
+
Next paragraph.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Multiline terms
|
|
80
|
+
|
|
81
|
+
Indented lines following a term are treated as part of the term:
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
: This is a
|
|
85
|
+
multiline term
|
|
86
|
+
: This is a
|
|
87
|
+
multiline definition
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Nested definition lists
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
: Outer term
|
|
94
|
+
: : Inner term
|
|
95
|
+
: Inner definition
|
|
96
|
+
: Next definition
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Options
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
type DlListOptions = {
|
|
103
|
+
/** Indent (spaces) required for dd lines. Default: 4 */
|
|
104
|
+
ddIndent?: number;
|
|
105
|
+
|
|
106
|
+
/** Require at least one dd unless dt-only is followed by blank line or EOF. Default: true */
|
|
107
|
+
requireDd?: boolean;
|
|
108
|
+
|
|
109
|
+
/** Stop parsing the current dl at the first blank line after items. Default: true */
|
|
110
|
+
breakOnBlankLine?: boolean;
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
md.use(dlList, {
|
|
118
|
+
ddIndent: 2,
|
|
119
|
+
requireDd: true,
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## What this plugin does NOT do
|
|
124
|
+
|
|
125
|
+
* Does not modify Markdown rendering outside definition lists
|
|
126
|
+
* Does not change markdown-it default paragraph behavior
|
|
127
|
+
* Does not attempt to support every existing definition list syntax variant
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
default: () => dlListPlugin
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
var RULE_NAME = "dl_list_colon";
|
|
26
|
+
var T_DL_OPEN = "dl_list_open";
|
|
27
|
+
var T_DL_CLOSE = "dl_list_close";
|
|
28
|
+
var T_DT_OPEN = "dl_dt_open";
|
|
29
|
+
var T_DT_CLOSE = "dl_dt_close";
|
|
30
|
+
var T_DD_OPEN = "dl_dd_open";
|
|
31
|
+
var T_DD_CLOSE = "dl_dd_close";
|
|
32
|
+
var DEFAULT_DD_INDENT = 4;
|
|
33
|
+
var DEFAULT_REQUIRE_DD = true;
|
|
34
|
+
var DEFAULT_BREAK_ON_BLANK = true;
|
|
35
|
+
function dlListPlugin(md, opts = {}) {
|
|
36
|
+
const ddIndent = clampInt(opts.ddIndent ?? DEFAULT_DD_INDENT, 1, 12);
|
|
37
|
+
const requireDd = opts.requireDd ?? DEFAULT_REQUIRE_DD;
|
|
38
|
+
const breakOnBlankLine = opts.breakOnBlankLine ?? DEFAULT_BREAK_ON_BLANK;
|
|
39
|
+
md.block.ruler.before("paragraph", RULE_NAME, (state, startLine, endLine, silent) => {
|
|
40
|
+
const begin = startLine;
|
|
41
|
+
const firstDt = readDtBlock(state, begin, endLine, ddIndent);
|
|
42
|
+
if (!firstDt) return false;
|
|
43
|
+
const firstHasDd = hasDdHeaderAtSameLevel(state, firstDt, ddIndent, endLine);
|
|
44
|
+
const firstDtOnlyOk = !firstHasDd && isDtOnlyBoundary(state, firstDt.nextLine, endLine);
|
|
45
|
+
if (requireDd && !firstHasDd && !firstDtOnlyOk) return false;
|
|
46
|
+
if (silent) return true;
|
|
47
|
+
const parsed = parseDlItems(state, begin, endLine, { ddIndent, requireDd, breakOnBlankLine });
|
|
48
|
+
if (!parsed) return false;
|
|
49
|
+
renderDlTokens(state, begin, parsed.endLine, parsed.items);
|
|
50
|
+
state.line = parsed.endLine;
|
|
51
|
+
return true;
|
|
52
|
+
});
|
|
53
|
+
md.renderer.rules[T_DL_OPEN] = () => "<dl>\n";
|
|
54
|
+
md.renderer.rules[T_DL_CLOSE] = () => "</dl>\n";
|
|
55
|
+
md.renderer.rules[T_DT_OPEN] = () => "<dt>";
|
|
56
|
+
md.renderer.rules[T_DT_CLOSE] = () => "</dt>\n";
|
|
57
|
+
md.renderer.rules[T_DD_OPEN] = () => "<dd>";
|
|
58
|
+
md.renderer.rules[T_DD_CLOSE] = () => "</dd>\n";
|
|
59
|
+
}
|
|
60
|
+
function parseDlItems(state, begin, endLine, ctx) {
|
|
61
|
+
const items = [];
|
|
62
|
+
let line = begin;
|
|
63
|
+
while (line < endLine) {
|
|
64
|
+
const dtBlock = readDtBlock(state, line, endLine, ctx.ddIndent);
|
|
65
|
+
if (!dtBlock) break;
|
|
66
|
+
const { dds, nextLineAfterDds } = collectDds(state, dtBlock, endLine, ctx.ddIndent);
|
|
67
|
+
if (dds.length === 0) {
|
|
68
|
+
const afterDt = dtBlock.nextLine;
|
|
69
|
+
const dtOnlyHere = isDtOnlyBoundary(state, afterDt, endLine);
|
|
70
|
+
if (ctx.requireDd && !dtOnlyHere) break;
|
|
71
|
+
items.push({ dtLine: line, dtText: dtBlock.text, dds: [] });
|
|
72
|
+
line = afterDt;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
items.push({ dtLine: line, dtText: dtBlock.text, dds });
|
|
76
|
+
line = nextLineAfterDds;
|
|
77
|
+
if (ctx.breakOnBlankLine && line < endLine && isBlankLine(state, line)) break;
|
|
78
|
+
}
|
|
79
|
+
if (items.length === 0) return null;
|
|
80
|
+
return { items, endLine: line };
|
|
81
|
+
}
|
|
82
|
+
function renderDlTokens(state, begin, endLine, items) {
|
|
83
|
+
const dlOpen = state.push(T_DL_OPEN, "dl", 1);
|
|
84
|
+
dlOpen.map = [begin, endLine];
|
|
85
|
+
for (const it of items) {
|
|
86
|
+
const dtOpen = state.push(T_DT_OPEN, "dt", 1);
|
|
87
|
+
dtOpen.map = [it.dtLine, it.dtLine + 1];
|
|
88
|
+
pushInline(state, it.dtText, it.dtLine);
|
|
89
|
+
state.push(T_DT_CLOSE, "dt", -1);
|
|
90
|
+
for (const d of it.dds) {
|
|
91
|
+
state.push(T_DD_OPEN, "dd", 1);
|
|
92
|
+
if (shouldBlockParseDd(d.text)) {
|
|
93
|
+
const ddText = looksLikeNestedDl(d.text) ? normalizeNestedDlText(d.text) : d.text;
|
|
94
|
+
const normalized = normalizeIndentedBlock(ddText);
|
|
95
|
+
parseDdContentIntoTokens(state, normalized);
|
|
96
|
+
} else {
|
|
97
|
+
pushInline(state, d.text, d.line);
|
|
98
|
+
}
|
|
99
|
+
state.push(T_DD_CLOSE, "dd", -1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
state.push(T_DL_CLOSE, "dl", -1);
|
|
103
|
+
}
|
|
104
|
+
function collectDds(state, dtBlock, endLine, ddIndent) {
|
|
105
|
+
const dds = [];
|
|
106
|
+
let next = dtBlock.nextLine;
|
|
107
|
+
while (next < endLine) {
|
|
108
|
+
if (isBlankLine(state, next)) break;
|
|
109
|
+
const ddBlock = readDdBlock(state, next, endLine, dtBlock.baseIndent, ddIndent);
|
|
110
|
+
if (!ddBlock) break;
|
|
111
|
+
dds.push({ line: next, text: ddBlock.text });
|
|
112
|
+
next = ddBlock.nextLine;
|
|
113
|
+
}
|
|
114
|
+
return { dds, nextLineAfterDds: next };
|
|
115
|
+
}
|
|
116
|
+
function hasDdHeaderAtSameLevel(state, dtBlock, ddIndent, endLine) {
|
|
117
|
+
const minIndent = dtBlock.baseIndent + ddIndent;
|
|
118
|
+
if (dtBlock.nextLine >= endLine) return false;
|
|
119
|
+
return !!parseDdHeaderAtLevel(state, dtBlock.nextLine, minIndent) || isEmptyDdHeaderAtLevel(state, dtBlock.nextLine, minIndent);
|
|
120
|
+
}
|
|
121
|
+
function isDtOnlyBoundary(state, line, endLine) {
|
|
122
|
+
return line >= endLine || isBlankLine(state, line);
|
|
123
|
+
}
|
|
124
|
+
function shouldBlockParseDd(text) {
|
|
125
|
+
return looksLikeNestedDl(text) || text.indexOf("\n") >= 0;
|
|
126
|
+
}
|
|
127
|
+
function looksLikeNestedDl(text) {
|
|
128
|
+
return text.replace(/^\s+/, "").indexOf(":") === 0;
|
|
129
|
+
}
|
|
130
|
+
function pushInline(state, text, line) {
|
|
131
|
+
const token = state.push("inline", "", 0);
|
|
132
|
+
token.map = [line, line + 1];
|
|
133
|
+
token.content = text;
|
|
134
|
+
token.children = [];
|
|
135
|
+
}
|
|
136
|
+
function isBlankLine(state, line) {
|
|
137
|
+
const start = state.bMarks[line] + state.tShift[line];
|
|
138
|
+
const end = state.eMarks[line];
|
|
139
|
+
return start >= end;
|
|
140
|
+
}
|
|
141
|
+
function parseDtLine(state, line) {
|
|
142
|
+
if (line >= state.lineMax) return null;
|
|
143
|
+
const raw = getLineText(state, line);
|
|
144
|
+
const m = raw.match(/^( {0,3}):[ \t]+(.+?)\s*$/);
|
|
145
|
+
return m ? { text: m[2] } : null;
|
|
146
|
+
}
|
|
147
|
+
function parseDdHeaderAtLevel(state, line, minIndent) {
|
|
148
|
+
if (line >= state.lineMax) return null;
|
|
149
|
+
const raw = getLineText(state, line);
|
|
150
|
+
const indent = countLeadingSpaces(raw);
|
|
151
|
+
if (indent < minIndent || indent > minIndent + 3) return null;
|
|
152
|
+
const re = new RegExp(`^( {${indent}})(::?)[ \\t]+(.+?)\\s*$`);
|
|
153
|
+
const m = raw.match(re);
|
|
154
|
+
if (!m) return null;
|
|
155
|
+
const marker = m[2];
|
|
156
|
+
return { text: m[3], isNestedDlStart: marker === "::" };
|
|
157
|
+
}
|
|
158
|
+
function isEmptyDdHeaderAtLevel(state, line, minIndent) {
|
|
159
|
+
if (line >= state.lineMax) return false;
|
|
160
|
+
const raw = getLineText(state, line);
|
|
161
|
+
const indent = countLeadingSpaces(raw);
|
|
162
|
+
if (indent < minIndent || indent > minIndent + 3) return false;
|
|
163
|
+
const re = new RegExp(`^( {${indent}})(::?)\\s*$`);
|
|
164
|
+
return re.test(raw);
|
|
165
|
+
}
|
|
166
|
+
function readDtBlock(state, startLine, endLine, ddMinIndent) {
|
|
167
|
+
const dt = parseDtLine(state, startLine);
|
|
168
|
+
if (!dt) return null;
|
|
169
|
+
const raw0 = getLineText(state, startLine);
|
|
170
|
+
const baseIndent = countLeadingSpaces(raw0);
|
|
171
|
+
const lines = [dt.text];
|
|
172
|
+
let line = startLine + 1;
|
|
173
|
+
while (line < endLine) {
|
|
174
|
+
if (isWhitespaceOnlyLine(state, line)) break;
|
|
175
|
+
if (parseDtLine(state, line)) break;
|
|
176
|
+
const minIndent = baseIndent + ddMinIndent;
|
|
177
|
+
if (parseDdHeaderAtLevel(state, line, minIndent) || isEmptyDdHeaderAtLevel(state, line, minIndent)) break;
|
|
178
|
+
const raw = getLineText(state, line);
|
|
179
|
+
const first = raw.charCodeAt(0);
|
|
180
|
+
const isSpaceOrTab = first === 32 || first === 9;
|
|
181
|
+
if (!isSpaceOrTab) break;
|
|
182
|
+
lines.push(stripUpTo(raw, baseIndent + 2).replace(/\s+$/, ""));
|
|
183
|
+
line++;
|
|
184
|
+
}
|
|
185
|
+
return { baseIndent, text: lines.join("\n"), nextLine: line };
|
|
186
|
+
}
|
|
187
|
+
function readDdBlock(state, startLine, endLine, baseIndent, ddIndent) {
|
|
188
|
+
const minIndent = baseIndent + ddIndent;
|
|
189
|
+
const dd0 = parseDdHeaderAtLevel(state, startLine, minIndent);
|
|
190
|
+
const emptyHeader = !dd0 && isEmptyDdHeaderAtLevel(state, startLine, minIndent);
|
|
191
|
+
if (!dd0 && !emptyHeader) return null;
|
|
192
|
+
const lines = [];
|
|
193
|
+
if (dd0) {
|
|
194
|
+
lines.push(dd0.isNestedDlStart ? `: ${dd0.text}` : dd0.text);
|
|
195
|
+
}
|
|
196
|
+
let line = startLine + 1;
|
|
197
|
+
while (line < endLine) {
|
|
198
|
+
if (isWhitespaceOnlyLine(state, line)) {
|
|
199
|
+
const next = line + 1;
|
|
200
|
+
if (next >= endLine) break;
|
|
201
|
+
if (isWhitespaceOnlyLine(state, next)) break;
|
|
202
|
+
if (parseDtLine(state, next)) break;
|
|
203
|
+
if (parseDdHeaderAtLevel(state, next, minIndent) || isEmptyDdHeaderAtLevel(state, next, minIndent)) break;
|
|
204
|
+
const rawNext = getLineText(state, next);
|
|
205
|
+
const indentNext = countLeadingSpaces(rawNext);
|
|
206
|
+
if (!emptyHeader && indentNext < minIndent) break;
|
|
207
|
+
lines.push("");
|
|
208
|
+
line++;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (parseDtLine(state, line)) break;
|
|
212
|
+
if (parseDdHeaderAtLevel(state, line, minIndent) || isEmptyDdHeaderAtLevel(state, line, minIndent)) break;
|
|
213
|
+
const raw = getLineText(state, line);
|
|
214
|
+
const indent = countLeadingSpaces(raw);
|
|
215
|
+
if (!emptyHeader && indent < minIndent) break;
|
|
216
|
+
const cut = emptyHeader ? Math.min(indent, minIndent) : minIndent;
|
|
217
|
+
lines.push(stripUpTo(raw, cut).replace(/\s+$/, ""));
|
|
218
|
+
line++;
|
|
219
|
+
}
|
|
220
|
+
return { text: lines.length === 0 ? "" : lines.join("\n"), nextLine: line };
|
|
221
|
+
}
|
|
222
|
+
function getLineText(state, line) {
|
|
223
|
+
const start = state.bMarks[line];
|
|
224
|
+
const end = state.eMarks[line];
|
|
225
|
+
return state.src.slice(start, end);
|
|
226
|
+
}
|
|
227
|
+
function isWhitespaceOnlyLine(state, line) {
|
|
228
|
+
return getLineText(state, line).trim().length === 0;
|
|
229
|
+
}
|
|
230
|
+
function countLeadingSpaces(s) {
|
|
231
|
+
let i = 0;
|
|
232
|
+
while (i < s.length && s.charCodeAt(i) === 32) i++;
|
|
233
|
+
return i;
|
|
234
|
+
}
|
|
235
|
+
function stripUpTo(s, n) {
|
|
236
|
+
let i = 0;
|
|
237
|
+
while (i < s.length && i < n && s.charCodeAt(i) === 32) i++;
|
|
238
|
+
return s.slice(i);
|
|
239
|
+
}
|
|
240
|
+
function clampInt(n, min, max) {
|
|
241
|
+
if (typeof n !== "number" || !isFinite(n)) return min;
|
|
242
|
+
return Math.max(min, Math.min(max, Math.floor(n)));
|
|
243
|
+
}
|
|
244
|
+
function normalizeNestedDlText(text) {
|
|
245
|
+
const lines = text.split("\n");
|
|
246
|
+
if (lines.length <= 1) return text;
|
|
247
|
+
for (let i = 1; i < lines.length; i++) {
|
|
248
|
+
const m = lines[i].match(/^(\s*):(.*)$/);
|
|
249
|
+
if (m) lines[i] = " :" + m[2];
|
|
250
|
+
}
|
|
251
|
+
return lines.join("\n");
|
|
252
|
+
}
|
|
253
|
+
function normalizeIndentedBlock(text) {
|
|
254
|
+
const lines = text.split("\n");
|
|
255
|
+
let min = Infinity;
|
|
256
|
+
for (const l of lines) {
|
|
257
|
+
if (l.trim().length === 0) continue;
|
|
258
|
+
const n = countLeadingSpaces(l);
|
|
259
|
+
if (n < min) min = n;
|
|
260
|
+
}
|
|
261
|
+
if (!isFinite(min) || min === 0) return text;
|
|
262
|
+
return lines.map((l) => l.trim().length === 0 ? "" : stripUpTo(l, min)).join("\n");
|
|
263
|
+
}
|
|
264
|
+
function parseDdContentIntoTokens(state, text) {
|
|
265
|
+
var _a, _b, _c;
|
|
266
|
+
const start = state.tokens.length;
|
|
267
|
+
state.md.block.parse(text, state.md, state.env, state.tokens);
|
|
268
|
+
const added = state.tokens.slice(start);
|
|
269
|
+
if (added.length === 3 && ((_a = added[0]) == null ? void 0 : _a.type) === "paragraph_open" && ((_b = added[1]) == null ? void 0 : _b.type) === "inline" && ((_c = added[2]) == null ? void 0 : _c.type) === "paragraph_close") {
|
|
270
|
+
state.tokens.length = start;
|
|
271
|
+
state.tokens.push(added[1]);
|
|
272
|
+
}
|
|
273
|
+
}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for the colon-based definition list plugin.
|
|
5
|
+
*/
|
|
6
|
+
type DlListOptions = {
|
|
7
|
+
/** Indent (spaces) required for dd lines. Default: 4 */
|
|
8
|
+
ddIndent?: number;
|
|
9
|
+
/** If true, dt-only items are allowed only when followed by blank line or EOF. Default: true */
|
|
10
|
+
requireDd?: boolean;
|
|
11
|
+
/** If true, stop parsing the current dl at the first blank line after items. Default: true */
|
|
12
|
+
breakOnBlankLine?: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare function dlListPlugin(md: MarkdownIt, opts?: DlListOptions): void;
|
|
15
|
+
|
|
16
|
+
export { type DlListOptions, dlListPlugin as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for the colon-based definition list plugin.
|
|
5
|
+
*/
|
|
6
|
+
type DlListOptions = {
|
|
7
|
+
/** Indent (spaces) required for dd lines. Default: 4 */
|
|
8
|
+
ddIndent?: number;
|
|
9
|
+
/** If true, dt-only items are allowed only when followed by blank line or EOF. Default: true */
|
|
10
|
+
requireDd?: boolean;
|
|
11
|
+
/** If true, stop parsing the current dl at the first blank line after items. Default: true */
|
|
12
|
+
breakOnBlankLine?: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare function dlListPlugin(md: MarkdownIt, opts?: DlListOptions): void;
|
|
15
|
+
|
|
16
|
+
export { type DlListOptions, dlListPlugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var RULE_NAME = "dl_list_colon";
|
|
3
|
+
var T_DL_OPEN = "dl_list_open";
|
|
4
|
+
var T_DL_CLOSE = "dl_list_close";
|
|
5
|
+
var T_DT_OPEN = "dl_dt_open";
|
|
6
|
+
var T_DT_CLOSE = "dl_dt_close";
|
|
7
|
+
var T_DD_OPEN = "dl_dd_open";
|
|
8
|
+
var T_DD_CLOSE = "dl_dd_close";
|
|
9
|
+
var DEFAULT_DD_INDENT = 4;
|
|
10
|
+
var DEFAULT_REQUIRE_DD = true;
|
|
11
|
+
var DEFAULT_BREAK_ON_BLANK = true;
|
|
12
|
+
function dlListPlugin(md, opts = {}) {
|
|
13
|
+
const ddIndent = clampInt(opts.ddIndent ?? DEFAULT_DD_INDENT, 1, 12);
|
|
14
|
+
const requireDd = opts.requireDd ?? DEFAULT_REQUIRE_DD;
|
|
15
|
+
const breakOnBlankLine = opts.breakOnBlankLine ?? DEFAULT_BREAK_ON_BLANK;
|
|
16
|
+
md.block.ruler.before("paragraph", RULE_NAME, (state, startLine, endLine, silent) => {
|
|
17
|
+
const begin = startLine;
|
|
18
|
+
const firstDt = readDtBlock(state, begin, endLine, ddIndent);
|
|
19
|
+
if (!firstDt) return false;
|
|
20
|
+
const firstHasDd = hasDdHeaderAtSameLevel(state, firstDt, ddIndent, endLine);
|
|
21
|
+
const firstDtOnlyOk = !firstHasDd && isDtOnlyBoundary(state, firstDt.nextLine, endLine);
|
|
22
|
+
if (requireDd && !firstHasDd && !firstDtOnlyOk) return false;
|
|
23
|
+
if (silent) return true;
|
|
24
|
+
const parsed = parseDlItems(state, begin, endLine, { ddIndent, requireDd, breakOnBlankLine });
|
|
25
|
+
if (!parsed) return false;
|
|
26
|
+
renderDlTokens(state, begin, parsed.endLine, parsed.items);
|
|
27
|
+
state.line = parsed.endLine;
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
md.renderer.rules[T_DL_OPEN] = () => "<dl>\n";
|
|
31
|
+
md.renderer.rules[T_DL_CLOSE] = () => "</dl>\n";
|
|
32
|
+
md.renderer.rules[T_DT_OPEN] = () => "<dt>";
|
|
33
|
+
md.renderer.rules[T_DT_CLOSE] = () => "</dt>\n";
|
|
34
|
+
md.renderer.rules[T_DD_OPEN] = () => "<dd>";
|
|
35
|
+
md.renderer.rules[T_DD_CLOSE] = () => "</dd>\n";
|
|
36
|
+
}
|
|
37
|
+
function parseDlItems(state, begin, endLine, ctx) {
|
|
38
|
+
const items = [];
|
|
39
|
+
let line = begin;
|
|
40
|
+
while (line < endLine) {
|
|
41
|
+
const dtBlock = readDtBlock(state, line, endLine, ctx.ddIndent);
|
|
42
|
+
if (!dtBlock) break;
|
|
43
|
+
const { dds, nextLineAfterDds } = collectDds(state, dtBlock, endLine, ctx.ddIndent);
|
|
44
|
+
if (dds.length === 0) {
|
|
45
|
+
const afterDt = dtBlock.nextLine;
|
|
46
|
+
const dtOnlyHere = isDtOnlyBoundary(state, afterDt, endLine);
|
|
47
|
+
if (ctx.requireDd && !dtOnlyHere) break;
|
|
48
|
+
items.push({ dtLine: line, dtText: dtBlock.text, dds: [] });
|
|
49
|
+
line = afterDt;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
items.push({ dtLine: line, dtText: dtBlock.text, dds });
|
|
53
|
+
line = nextLineAfterDds;
|
|
54
|
+
if (ctx.breakOnBlankLine && line < endLine && isBlankLine(state, line)) break;
|
|
55
|
+
}
|
|
56
|
+
if (items.length === 0) return null;
|
|
57
|
+
return { items, endLine: line };
|
|
58
|
+
}
|
|
59
|
+
function renderDlTokens(state, begin, endLine, items) {
|
|
60
|
+
const dlOpen = state.push(T_DL_OPEN, "dl", 1);
|
|
61
|
+
dlOpen.map = [begin, endLine];
|
|
62
|
+
for (const it of items) {
|
|
63
|
+
const dtOpen = state.push(T_DT_OPEN, "dt", 1);
|
|
64
|
+
dtOpen.map = [it.dtLine, it.dtLine + 1];
|
|
65
|
+
pushInline(state, it.dtText, it.dtLine);
|
|
66
|
+
state.push(T_DT_CLOSE, "dt", -1);
|
|
67
|
+
for (const d of it.dds) {
|
|
68
|
+
state.push(T_DD_OPEN, "dd", 1);
|
|
69
|
+
if (shouldBlockParseDd(d.text)) {
|
|
70
|
+
const ddText = looksLikeNestedDl(d.text) ? normalizeNestedDlText(d.text) : d.text;
|
|
71
|
+
const normalized = normalizeIndentedBlock(ddText);
|
|
72
|
+
parseDdContentIntoTokens(state, normalized);
|
|
73
|
+
} else {
|
|
74
|
+
pushInline(state, d.text, d.line);
|
|
75
|
+
}
|
|
76
|
+
state.push(T_DD_CLOSE, "dd", -1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
state.push(T_DL_CLOSE, "dl", -1);
|
|
80
|
+
}
|
|
81
|
+
function collectDds(state, dtBlock, endLine, ddIndent) {
|
|
82
|
+
const dds = [];
|
|
83
|
+
let next = dtBlock.nextLine;
|
|
84
|
+
while (next < endLine) {
|
|
85
|
+
if (isBlankLine(state, next)) break;
|
|
86
|
+
const ddBlock = readDdBlock(state, next, endLine, dtBlock.baseIndent, ddIndent);
|
|
87
|
+
if (!ddBlock) break;
|
|
88
|
+
dds.push({ line: next, text: ddBlock.text });
|
|
89
|
+
next = ddBlock.nextLine;
|
|
90
|
+
}
|
|
91
|
+
return { dds, nextLineAfterDds: next };
|
|
92
|
+
}
|
|
93
|
+
function hasDdHeaderAtSameLevel(state, dtBlock, ddIndent, endLine) {
|
|
94
|
+
const minIndent = dtBlock.baseIndent + ddIndent;
|
|
95
|
+
if (dtBlock.nextLine >= endLine) return false;
|
|
96
|
+
return !!parseDdHeaderAtLevel(state, dtBlock.nextLine, minIndent) || isEmptyDdHeaderAtLevel(state, dtBlock.nextLine, minIndent);
|
|
97
|
+
}
|
|
98
|
+
function isDtOnlyBoundary(state, line, endLine) {
|
|
99
|
+
return line >= endLine || isBlankLine(state, line);
|
|
100
|
+
}
|
|
101
|
+
function shouldBlockParseDd(text) {
|
|
102
|
+
return looksLikeNestedDl(text) || text.indexOf("\n") >= 0;
|
|
103
|
+
}
|
|
104
|
+
function looksLikeNestedDl(text) {
|
|
105
|
+
return text.replace(/^\s+/, "").indexOf(":") === 0;
|
|
106
|
+
}
|
|
107
|
+
function pushInline(state, text, line) {
|
|
108
|
+
const token = state.push("inline", "", 0);
|
|
109
|
+
token.map = [line, line + 1];
|
|
110
|
+
token.content = text;
|
|
111
|
+
token.children = [];
|
|
112
|
+
}
|
|
113
|
+
function isBlankLine(state, line) {
|
|
114
|
+
const start = state.bMarks[line] + state.tShift[line];
|
|
115
|
+
const end = state.eMarks[line];
|
|
116
|
+
return start >= end;
|
|
117
|
+
}
|
|
118
|
+
function parseDtLine(state, line) {
|
|
119
|
+
if (line >= state.lineMax) return null;
|
|
120
|
+
const raw = getLineText(state, line);
|
|
121
|
+
const m = raw.match(/^( {0,3}):[ \t]+(.+?)\s*$/);
|
|
122
|
+
return m ? { text: m[2] } : null;
|
|
123
|
+
}
|
|
124
|
+
function parseDdHeaderAtLevel(state, line, minIndent) {
|
|
125
|
+
if (line >= state.lineMax) return null;
|
|
126
|
+
const raw = getLineText(state, line);
|
|
127
|
+
const indent = countLeadingSpaces(raw);
|
|
128
|
+
if (indent < minIndent || indent > minIndent + 3) return null;
|
|
129
|
+
const re = new RegExp(`^( {${indent}})(::?)[ \\t]+(.+?)\\s*$`);
|
|
130
|
+
const m = raw.match(re);
|
|
131
|
+
if (!m) return null;
|
|
132
|
+
const marker = m[2];
|
|
133
|
+
return { text: m[3], isNestedDlStart: marker === "::" };
|
|
134
|
+
}
|
|
135
|
+
function isEmptyDdHeaderAtLevel(state, line, minIndent) {
|
|
136
|
+
if (line >= state.lineMax) return false;
|
|
137
|
+
const raw = getLineText(state, line);
|
|
138
|
+
const indent = countLeadingSpaces(raw);
|
|
139
|
+
if (indent < minIndent || indent > minIndent + 3) return false;
|
|
140
|
+
const re = new RegExp(`^( {${indent}})(::?)\\s*$`);
|
|
141
|
+
return re.test(raw);
|
|
142
|
+
}
|
|
143
|
+
function readDtBlock(state, startLine, endLine, ddMinIndent) {
|
|
144
|
+
const dt = parseDtLine(state, startLine);
|
|
145
|
+
if (!dt) return null;
|
|
146
|
+
const raw0 = getLineText(state, startLine);
|
|
147
|
+
const baseIndent = countLeadingSpaces(raw0);
|
|
148
|
+
const lines = [dt.text];
|
|
149
|
+
let line = startLine + 1;
|
|
150
|
+
while (line < endLine) {
|
|
151
|
+
if (isWhitespaceOnlyLine(state, line)) break;
|
|
152
|
+
if (parseDtLine(state, line)) break;
|
|
153
|
+
const minIndent = baseIndent + ddMinIndent;
|
|
154
|
+
if (parseDdHeaderAtLevel(state, line, minIndent) || isEmptyDdHeaderAtLevel(state, line, minIndent)) break;
|
|
155
|
+
const raw = getLineText(state, line);
|
|
156
|
+
const first = raw.charCodeAt(0);
|
|
157
|
+
const isSpaceOrTab = first === 32 || first === 9;
|
|
158
|
+
if (!isSpaceOrTab) break;
|
|
159
|
+
lines.push(stripUpTo(raw, baseIndent + 2).replace(/\s+$/, ""));
|
|
160
|
+
line++;
|
|
161
|
+
}
|
|
162
|
+
return { baseIndent, text: lines.join("\n"), nextLine: line };
|
|
163
|
+
}
|
|
164
|
+
function readDdBlock(state, startLine, endLine, baseIndent, ddIndent) {
|
|
165
|
+
const minIndent = baseIndent + ddIndent;
|
|
166
|
+
const dd0 = parseDdHeaderAtLevel(state, startLine, minIndent);
|
|
167
|
+
const emptyHeader = !dd0 && isEmptyDdHeaderAtLevel(state, startLine, minIndent);
|
|
168
|
+
if (!dd0 && !emptyHeader) return null;
|
|
169
|
+
const lines = [];
|
|
170
|
+
if (dd0) {
|
|
171
|
+
lines.push(dd0.isNestedDlStart ? `: ${dd0.text}` : dd0.text);
|
|
172
|
+
}
|
|
173
|
+
let line = startLine + 1;
|
|
174
|
+
while (line < endLine) {
|
|
175
|
+
if (isWhitespaceOnlyLine(state, line)) {
|
|
176
|
+
const next = line + 1;
|
|
177
|
+
if (next >= endLine) break;
|
|
178
|
+
if (isWhitespaceOnlyLine(state, next)) break;
|
|
179
|
+
if (parseDtLine(state, next)) break;
|
|
180
|
+
if (parseDdHeaderAtLevel(state, next, minIndent) || isEmptyDdHeaderAtLevel(state, next, minIndent)) break;
|
|
181
|
+
const rawNext = getLineText(state, next);
|
|
182
|
+
const indentNext = countLeadingSpaces(rawNext);
|
|
183
|
+
if (!emptyHeader && indentNext < minIndent) break;
|
|
184
|
+
lines.push("");
|
|
185
|
+
line++;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (parseDtLine(state, line)) break;
|
|
189
|
+
if (parseDdHeaderAtLevel(state, line, minIndent) || isEmptyDdHeaderAtLevel(state, line, minIndent)) break;
|
|
190
|
+
const raw = getLineText(state, line);
|
|
191
|
+
const indent = countLeadingSpaces(raw);
|
|
192
|
+
if (!emptyHeader && indent < minIndent) break;
|
|
193
|
+
const cut = emptyHeader ? Math.min(indent, minIndent) : minIndent;
|
|
194
|
+
lines.push(stripUpTo(raw, cut).replace(/\s+$/, ""));
|
|
195
|
+
line++;
|
|
196
|
+
}
|
|
197
|
+
return { text: lines.length === 0 ? "" : lines.join("\n"), nextLine: line };
|
|
198
|
+
}
|
|
199
|
+
function getLineText(state, line) {
|
|
200
|
+
const start = state.bMarks[line];
|
|
201
|
+
const end = state.eMarks[line];
|
|
202
|
+
return state.src.slice(start, end);
|
|
203
|
+
}
|
|
204
|
+
function isWhitespaceOnlyLine(state, line) {
|
|
205
|
+
return getLineText(state, line).trim().length === 0;
|
|
206
|
+
}
|
|
207
|
+
function countLeadingSpaces(s) {
|
|
208
|
+
let i = 0;
|
|
209
|
+
while (i < s.length && s.charCodeAt(i) === 32) i++;
|
|
210
|
+
return i;
|
|
211
|
+
}
|
|
212
|
+
function stripUpTo(s, n) {
|
|
213
|
+
let i = 0;
|
|
214
|
+
while (i < s.length && i < n && s.charCodeAt(i) === 32) i++;
|
|
215
|
+
return s.slice(i);
|
|
216
|
+
}
|
|
217
|
+
function clampInt(n, min, max) {
|
|
218
|
+
if (typeof n !== "number" || !isFinite(n)) return min;
|
|
219
|
+
return Math.max(min, Math.min(max, Math.floor(n)));
|
|
220
|
+
}
|
|
221
|
+
function normalizeNestedDlText(text) {
|
|
222
|
+
const lines = text.split("\n");
|
|
223
|
+
if (lines.length <= 1) return text;
|
|
224
|
+
for (let i = 1; i < lines.length; i++) {
|
|
225
|
+
const m = lines[i].match(/^(\s*):(.*)$/);
|
|
226
|
+
if (m) lines[i] = " :" + m[2];
|
|
227
|
+
}
|
|
228
|
+
return lines.join("\n");
|
|
229
|
+
}
|
|
230
|
+
function normalizeIndentedBlock(text) {
|
|
231
|
+
const lines = text.split("\n");
|
|
232
|
+
let min = Infinity;
|
|
233
|
+
for (const l of lines) {
|
|
234
|
+
if (l.trim().length === 0) continue;
|
|
235
|
+
const n = countLeadingSpaces(l);
|
|
236
|
+
if (n < min) min = n;
|
|
237
|
+
}
|
|
238
|
+
if (!isFinite(min) || min === 0) return text;
|
|
239
|
+
return lines.map((l) => l.trim().length === 0 ? "" : stripUpTo(l, min)).join("\n");
|
|
240
|
+
}
|
|
241
|
+
function parseDdContentIntoTokens(state, text) {
|
|
242
|
+
var _a, _b, _c;
|
|
243
|
+
const start = state.tokens.length;
|
|
244
|
+
state.md.block.parse(text, state.md, state.env, state.tokens);
|
|
245
|
+
const added = state.tokens.slice(start);
|
|
246
|
+
if (added.length === 3 && ((_a = added[0]) == null ? void 0 : _a.type) === "paragraph_open" && ((_b = added[1]) == null ? void 0 : _b.type) === "inline" && ((_c = added[2]) == null ? void 0 : _c.type) === "paragraph_close") {
|
|
247
|
+
state.tokens.length = start;
|
|
248
|
+
state.tokens.push(added[1]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
export {
|
|
252
|
+
dlListPlugin as default
|
|
253
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "markdown-it-dl-list",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "markdown-it plugin for colon-based definition lists (<dl>, <dt>, <dd>)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/kanemu/dl-list-markdown"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/kanemu/dl-list-markdown/tree/main/packages/markdown-it-dl-list",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/kanemu/dl-list-markdown/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"markdown-it",
|
|
17
|
+
"markdown",
|
|
18
|
+
"definition-list",
|
|
19
|
+
"dl",
|
|
20
|
+
"dt",
|
|
21
|
+
"dd",
|
|
22
|
+
"plugin"
|
|
23
|
+
],
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"require": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/**",
|
|
36
|
+
"README.md",
|
|
37
|
+
"README-ja.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"markdown-it": "^14.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"markdown-it": {
|
|
46
|
+
"optional": false
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/markdown-it": "^14.1.2",
|
|
51
|
+
"markdown-it": "^14.1.0",
|
|
52
|
+
"rimraf": "^6.1.2",
|
|
53
|
+
"tsup": "^8.5.1",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^4.0.17"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"dev": "vitest",
|
|
61
|
+
"clean": "rimraf dist"
|
|
62
|
+
}
|
|
63
|
+
}
|