markdown-it-admon-collapsible 1.9.4 → 1.9.5
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/CHANGELOG.md +14 -7
- package/README.md +24 -13
- package/index.js +115 -105
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.9.
|
|
3
|
+
## 1.9.5
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
2026-01-01
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- Made validation customizable
|
|
8
|
+
- General cleanup and added markdownlint
|
|
9
|
+
|
|
10
|
+
## 1.9.4
|
|
11
|
+
|
|
12
|
+
2025-12-27
|
|
13
|
+
|
|
14
|
+
- Reverted since the previous change was a misunderstanding
|
|
15
|
+
- Using details/summary for collapsible admonitions
|
|
8
16
|
|
|
9
17
|
## 1.9.3
|
|
10
18
|
|
|
@@ -12,9 +20,8 @@
|
|
|
12
20
|
|
|
13
21
|
- Changed the generated HTML to be more like markdown-it-container
|
|
14
22
|
|
|
15
|
-
## 1.9.
|
|
23
|
+
## 1.9.2
|
|
16
24
|
|
|
17
|
-
2025-12-
|
|
25
|
+
2025-12-26
|
|
18
26
|
|
|
19
|
-
-
|
|
20
|
-
- Using details/summary for collapsible admonitions
|
|
27
|
+
- forked from markdown-it-admon
|
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# markdown-it-admon-collapsible
|
|
2
2
|
|
|
3
3
|
> **Note:** This package is a fork of [markdown-it-admon](https://github.com/commenthol/markdown-it-admon) with added support for collapsible blocks (???), inspired by Material for MkDocs.
|
|
4
|
-
|
|
5
4
|
> Plugin for creating admonitions for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser.
|
|
6
5
|
|
|
7
6
|
With this plugin you can have collapsible admonitions:
|
|
@@ -31,7 +30,7 @@ Markdown syntax is inspired by [Material for MkDocs](https://squidfunk.github.io
|
|
|
31
30
|
|
|
32
31
|
A styles file does support the following admonition types: Credits go to [vscode-markdown-extended][].
|
|
33
32
|
|
|
34
|
-
```
|
|
33
|
+
```text
|
|
35
34
|
'note',
|
|
36
35
|
'summary', 'abstract', 'tldr',
|
|
37
36
|
'info', 'todo',
|
|
@@ -45,17 +44,16 @@ A styles file does support the following admonition types: Credits go to [vscode
|
|
|
45
44
|
'quote', 'cite'
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-

|
|
47
|
+

|
|
49
48
|
|
|
50
49
|
## Installation
|
|
51
50
|
|
|
52
51
|
node.js:
|
|
53
52
|
|
|
54
53
|
```bash
|
|
55
|
-
|
|
54
|
+
npm install markdown-it-admon-collapsible --save
|
|
56
55
|
```
|
|
57
56
|
|
|
58
|
-
|
|
59
57
|
## API
|
|
60
58
|
|
|
61
59
|
```js
|
|
@@ -63,11 +61,24 @@ const md = require('markdown-it')()
|
|
|
63
61
|
.use(require('markdown-it-admon-collapsible') [, options]);
|
|
64
62
|
```
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Plugin Options
|
|
65
|
+
|
|
66
|
+
- `render`: (optional) Custom render function.
|
|
67
|
+
- `validate`: (optional) Custom validation function. If provided, this function will be used to validate the parameters for admonitions.
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
### Example usage
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
const md = require('markdown-it')();
|
|
73
|
+
const admonitionPlugin = require('markdown-it-admon-collapsible');
|
|
74
|
+
|
|
75
|
+
md.use(admonitionPlugin, {
|
|
76
|
+
validate: function(params) {
|
|
77
|
+
// Custom validation logic
|
|
78
|
+
return params.startsWith('note');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
```
|
|
71
82
|
|
|
72
83
|
## License
|
|
73
84
|
|
|
@@ -75,7 +86,7 @@ Params:
|
|
|
75
86
|
|
|
76
87
|
## References
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
- [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/reference/admonitions/)
|
|
90
|
+
- [vscode-markdown-extended][vscode-markdown-extended]
|
|
91
|
+
- [rST]: https://docutils.sourceforge.io/docs/ref/rst/directives.html#specific-admonitions
|
|
92
|
+
- [vscode-markdown-extended]: https://github.com/qjebbs/vscode-markdown-extended
|
package/index.js
CHANGED
|
@@ -25,18 +25,18 @@ function getTag (params) {
|
|
|
25
25
|
return {}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const joined = _title.join(' ')
|
|
29
|
-
let title
|
|
28
|
+
const joined = _title.join(' ')
|
|
29
|
+
let title
|
|
30
30
|
if (!joined) {
|
|
31
|
-
title = capitalize(tag)
|
|
31
|
+
title = capitalize(tag)
|
|
32
32
|
} else if (joined === '""') {
|
|
33
|
-
title = ''
|
|
33
|
+
title = ''
|
|
34
34
|
} else if ((joined.startsWith('"') && joined.endsWith('"')) || (joined.startsWith("'") && joined.endsWith("'"))) {
|
|
35
|
-
title = joined.slice(1, -1)
|
|
35
|
+
title = joined.slice(1, -1)
|
|
36
36
|
} else {
|
|
37
|
-
title = joined
|
|
37
|
+
title = joined
|
|
38
38
|
}
|
|
39
|
-
return { tag: tag.toLowerCase(), title }
|
|
39
|
+
return { tag: tag.toLowerCase(), title }
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function validate (params) {
|
|
@@ -44,171 +44,181 @@ function validate (params) {
|
|
|
44
44
|
return !!tag
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function renderDefault(tokens, idx, _options, env, slf) {
|
|
48
|
-
return slf.renderToken(tokens, idx, _options, env, slf)
|
|
47
|
+
function renderDefault (tokens, idx, _options, env, slf) {
|
|
48
|
+
return slf.renderToken(tokens, idx, _options, env, slf)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function renderCollapsibleContentOpen(tokens, idx) {
|
|
52
|
-
return ''
|
|
51
|
+
function renderCollapsibleContentOpen (tokens, idx) {
|
|
52
|
+
return ''
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function renderCollapsibleContentClose() {
|
|
56
|
-
return ''
|
|
55
|
+
function renderCollapsibleContentClose () {
|
|
56
|
+
return ''
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
const minMarkers = 3
|
|
59
|
+
const minMarkers = 3
|
|
60
60
|
const markerTypes = [
|
|
61
61
|
{ str: '!', type: 'admonition' },
|
|
62
62
|
{ str: '?', type: 'collapsible' }
|
|
63
|
-
]
|
|
63
|
+
]
|
|
64
64
|
|
|
65
|
-
function admonition(state, startLine, endLine, silent) {
|
|
66
|
-
let pos, nextLine, token
|
|
67
|
-
const start = state.bMarks[startLine] + state.tShift[startLine]
|
|
68
|
-
let max = state.eMarks[startLine]
|
|
65
|
+
function admonition (state, startLine, endLine, silent) {
|
|
66
|
+
let pos, nextLine, token
|
|
67
|
+
const start = state.bMarks[startLine] + state.tShift[startLine]
|
|
68
|
+
let max = state.eMarks[startLine]
|
|
69
69
|
|
|
70
70
|
// Determine marker type
|
|
71
|
-
let markerType = null
|
|
71
|
+
let markerType = null
|
|
72
72
|
for (const type of markerTypes) {
|
|
73
73
|
if (state.src.substr(start, type.str.length) === type.str.repeat(type.str.length)) {
|
|
74
|
-
markerType = type
|
|
75
|
-
break
|
|
74
|
+
markerType = type
|
|
75
|
+
break
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
if (!markerType) return false
|
|
78
|
+
if (!markerType) return false
|
|
79
79
|
|
|
80
|
-
const markerStr = markerType.str
|
|
81
|
-
const markerLen = markerStr.length
|
|
80
|
+
const markerStr = markerType.str
|
|
81
|
+
const markerLen = markerStr.length
|
|
82
82
|
|
|
83
83
|
// Check out the rest of the marker string
|
|
84
84
|
for (pos = start + 1; pos <= max; pos++) {
|
|
85
85
|
if (markerStr[(pos - start) % markerLen] !== state.src[pos]) {
|
|
86
|
-
break
|
|
86
|
+
break
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const markerCount = Math.floor((pos - start) / markerLen)
|
|
91
|
-
if (markerCount < minMarkers) return false
|
|
92
|
-
const markerPos = pos - ((pos - start) % markerLen)
|
|
93
|
-
let params = state.src.slice(markerPos, max)
|
|
94
|
-
let markup = state.src.slice(start, markerPos)
|
|
90
|
+
const markerCount = Math.floor((pos - start) / markerLen)
|
|
91
|
+
if (markerCount < minMarkers) return false
|
|
92
|
+
const markerPos = pos - ((pos - start) % markerLen)
|
|
93
|
+
let params = state.src.slice(markerPos, max)
|
|
94
|
+
let markup = state.src.slice(start, markerPos)
|
|
95
95
|
|
|
96
96
|
// Collapsible: check for plus sign
|
|
97
|
-
|
|
98
|
-
let expanded = false
|
|
97
|
+
const isCollapsible = markerType.type === 'collapsible'
|
|
98
|
+
let expanded = false
|
|
99
99
|
if (isCollapsible && params.trim().startsWith('+')) {
|
|
100
|
-
expanded = true
|
|
101
|
-
params = params.trim().slice(1).trim()
|
|
102
|
-
markup += '+'
|
|
100
|
+
expanded = true
|
|
101
|
+
params = params.trim().slice(1).trim()
|
|
102
|
+
markup += '+'
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const validateFn = state.md.options && state.md.options.admonitionValidate
|
|
106
|
+
? state.md.options.admonitionValidate
|
|
107
|
+
: validate
|
|
108
|
+
if (!validateFn(params)) return false
|
|
109
|
+
if (silent) return true
|
|
107
110
|
|
|
108
|
-
const oldParent = state.parentType
|
|
109
|
-
const oldLineMax = state.lineMax
|
|
110
|
-
const oldIndent = state.blkIndent
|
|
111
|
+
const oldParent = state.parentType
|
|
112
|
+
const oldLineMax = state.lineMax
|
|
113
|
+
const oldIndent = state.blkIndent
|
|
111
114
|
|
|
112
|
-
let blkStart = pos
|
|
115
|
+
let blkStart = pos
|
|
113
116
|
for (; blkStart < max; blkStart += 1) {
|
|
114
|
-
if (state.src[blkStart] !== ' ') break
|
|
117
|
+
if (state.src[blkStart] !== ' ') break
|
|
115
118
|
}
|
|
116
|
-
state.parentType = 'admonition'
|
|
117
|
-
state.blkIndent += blkStart - start
|
|
119
|
+
state.parentType = 'admonition'
|
|
120
|
+
state.blkIndent += blkStart - start
|
|
118
121
|
|
|
119
|
-
let wasEmpty = false
|
|
120
|
-
nextLine = startLine
|
|
122
|
+
let wasEmpty = false
|
|
123
|
+
nextLine = startLine
|
|
121
124
|
for (;;) {
|
|
122
|
-
nextLine
|
|
123
|
-
if (nextLine >= endLine) break
|
|
124
|
-
pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
125
|
-
max = state.eMarks[nextLine]
|
|
126
|
-
const isEmpty = state.sCount[nextLine] < state.blkIndent
|
|
127
|
-
if (isEmpty && wasEmpty) break
|
|
128
|
-
wasEmpty = isEmpty
|
|
129
|
-
if (pos < max && state.sCount[nextLine] < state.blkIndent) break
|
|
125
|
+
nextLine++
|
|
126
|
+
if (nextLine >= endLine) break
|
|
127
|
+
pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
128
|
+
max = state.eMarks[nextLine]
|
|
129
|
+
const isEmpty = state.sCount[nextLine] < state.blkIndent
|
|
130
|
+
if (isEmpty && wasEmpty) break
|
|
131
|
+
wasEmpty = isEmpty
|
|
132
|
+
if (pos < max && state.sCount[nextLine] < state.blkIndent) break
|
|
130
133
|
}
|
|
131
|
-
state.lineMax = nextLine
|
|
134
|
+
state.lineMax = nextLine
|
|
132
135
|
|
|
133
|
-
const { tag, title } = getTag(params)
|
|
136
|
+
const { tag, title } = getTag(params)
|
|
134
137
|
|
|
135
138
|
// Use different token for collapsible
|
|
136
|
-
const openType = isCollapsible ? 'collapsible_open' : 'admonition_open'
|
|
137
|
-
const closeType = isCollapsible ? 'collapsible_close' : 'admonition_close'
|
|
139
|
+
const openType = isCollapsible ? 'collapsible_open' : 'admonition_open'
|
|
140
|
+
const closeType = isCollapsible ? 'collapsible_close' : 'admonition_close'
|
|
138
141
|
|
|
139
142
|
token = isCollapsible ? state.push(openType, 'details', 1) : state.push(openType, 'div', 1)
|
|
140
|
-
token.markup = markup
|
|
141
|
-
token.block = true
|
|
142
|
-
token.attrs = [['class', `admonition ${tag}`]]
|
|
143
|
+
token.markup = markup
|
|
144
|
+
token.block = true
|
|
145
|
+
token.attrs = [['class', `admonition ${tag}`]]
|
|
143
146
|
if (expanded) {
|
|
144
|
-
token.attrs.push(['open', ''])
|
|
147
|
+
token.attrs.push(['open', ''])
|
|
145
148
|
}
|
|
146
|
-
token.meta = { tag, expanded }
|
|
147
|
-
token.content = title
|
|
148
|
-
token.info = params
|
|
149
|
-
token.map = [startLine, nextLine]
|
|
149
|
+
token.meta = { tag, expanded }
|
|
150
|
+
token.content = title
|
|
151
|
+
token.info = params
|
|
152
|
+
token.map = [startLine, nextLine]
|
|
150
153
|
|
|
151
154
|
if (title) {
|
|
152
|
-
const titleMarkup = markup + ' ' + tag
|
|
155
|
+
const titleMarkup = markup + ' ' + tag
|
|
153
156
|
token = isCollapsible ? state.push('collapsible_title_open', 'summary', 1) : state.push('admonition_title_open', 'p', 1)
|
|
154
|
-
token.markup = titleMarkup
|
|
155
|
-
token.attrs = [['class', 'admonition-title']]
|
|
156
|
-
token.map = [startLine, startLine + 1]
|
|
157
|
+
token.markup = titleMarkup
|
|
158
|
+
token.attrs = [['class', 'admonition-title']]
|
|
159
|
+
token.map = [startLine, startLine + 1]
|
|
157
160
|
|
|
158
|
-
token = state.push('inline', '', 0)
|
|
159
|
-
token.content = title
|
|
160
|
-
token.map = [startLine, startLine + 1]
|
|
161
|
-
token.children = []
|
|
161
|
+
token = state.push('inline', '', 0)
|
|
162
|
+
token.content = title
|
|
163
|
+
token.map = [startLine, startLine + 1]
|
|
164
|
+
token.children = []
|
|
162
165
|
|
|
163
|
-
token = isCollapsible ? state.push('collapsible_title_close', 'summary', -1) : state.push('admonition_title_close', 'p', -1)
|
|
164
|
-
token.markup = titleMarkup
|
|
166
|
+
token = isCollapsible ? state.push('collapsible_title_close', 'summary', -1) : state.push('admonition_title_close', 'p', -1)
|
|
167
|
+
token.markup = titleMarkup
|
|
165
168
|
}
|
|
166
169
|
|
|
167
|
-
state.md.block.tokenize(state, startLine + 1, nextLine)
|
|
170
|
+
state.md.block.tokenize(state, startLine + 1, nextLine)
|
|
168
171
|
|
|
169
|
-
token = state.push(closeType, 'div', -1)
|
|
170
|
-
token.markup = state.src.slice(start, pos)
|
|
171
|
-
token.block = true
|
|
172
|
+
token = state.push(closeType, 'div', -1)
|
|
173
|
+
token.markup = state.src.slice(start, pos)
|
|
174
|
+
token.block = true
|
|
172
175
|
|
|
173
|
-
state.parentType = oldParent
|
|
174
|
-
state.lineMax = oldLineMax
|
|
175
|
-
state.blkIndent = oldIndent
|
|
176
|
-
state.line = nextLine
|
|
176
|
+
state.parentType = oldParent
|
|
177
|
+
state.lineMax = oldLineMax
|
|
178
|
+
state.blkIndent = oldIndent
|
|
179
|
+
state.line = nextLine
|
|
177
180
|
|
|
178
|
-
return true
|
|
181
|
+
return true
|
|
179
182
|
}
|
|
180
183
|
|
|
181
|
-
|
|
182
|
-
const render = options.render || renderDefault
|
|
184
|
+
function admonitionPlugin (md, options = {}) {
|
|
185
|
+
const render = options.render || renderDefault
|
|
186
|
+
|
|
187
|
+
if (options.validate) {
|
|
188
|
+
md.options.admonitionValidate = options.validate
|
|
189
|
+
}
|
|
183
190
|
|
|
184
|
-
md.renderer.rules.admonition_open = render
|
|
185
|
-
md.renderer.rules.admonition_close = render
|
|
186
|
-
md.renderer.rules.admonition_title_open = render
|
|
187
|
-
md.renderer.rules.admonition_title_close = render
|
|
191
|
+
md.renderer.rules.admonition_open = render
|
|
192
|
+
md.renderer.rules.admonition_close = render
|
|
193
|
+
md.renderer.rules.admonition_title_open = render
|
|
194
|
+
md.renderer.rules.admonition_title_close = render
|
|
188
195
|
|
|
189
196
|
// Collapsible rendering
|
|
190
197
|
// Use default renderer for collapsible_open/close
|
|
191
|
-
md.renderer.rules.collapsible_close = (tokens, idx) => '</details>\n'
|
|
198
|
+
md.renderer.rules.collapsible_close = (tokens, idx) => '</details>\n'
|
|
192
199
|
// Use default renderer for collapsible_title_open/close
|
|
193
200
|
|
|
194
201
|
// Wrap content in collapsible-content div
|
|
195
|
-
const origBlockTokenize = md.block.tokenize
|
|
196
|
-
md.block.tokenize = function(state, startLine, endLine) {
|
|
202
|
+
const origBlockTokenize = md.block.tokenize
|
|
203
|
+
md.block.tokenize = function (state, startLine, endLine) {
|
|
197
204
|
if (state.parentType === 'admonition' && state.tokens.length > 0) {
|
|
198
|
-
const lastToken = state.tokens[state.tokens.length - 1]
|
|
205
|
+
const lastToken = state.tokens[state.tokens.length - 1]
|
|
199
206
|
if (lastToken.type === 'collapsible_title_close') {
|
|
200
|
-
state.tokens.push({ type: 'collapsible_content_open' })
|
|
201
|
-
origBlockTokenize.call(this, state, startLine, endLine)
|
|
202
|
-
state.tokens.push({ type: 'collapsible_content_close' })
|
|
203
|
-
return
|
|
207
|
+
state.tokens.push({ type: 'collapsible_content_open' })
|
|
208
|
+
origBlockTokenize.call(this, state, startLine, endLine)
|
|
209
|
+
state.tokens.push({ type: 'collapsible_content_close' })
|
|
210
|
+
return
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
|
-
origBlockTokenize.call(this, state, startLine, endLine)
|
|
207
|
-
}
|
|
208
|
-
md.renderer.rules.collapsible_content_open = renderCollapsibleContentOpen
|
|
209
|
-
md.renderer.rules.collapsible_content_close = renderCollapsibleContentClose
|
|
213
|
+
origBlockTokenize.call(this, state, startLine, endLine)
|
|
214
|
+
}
|
|
215
|
+
md.renderer.rules.collapsible_content_open = renderCollapsibleContentOpen
|
|
216
|
+
md.renderer.rules.collapsible_content_close = renderCollapsibleContentClose
|
|
210
217
|
|
|
211
218
|
md.block.ruler.before('fence', 'admonition', admonition, {
|
|
212
219
|
alt: ['paragraph', 'reference', 'blockquote', 'list']
|
|
213
|
-
})
|
|
220
|
+
})
|
|
214
221
|
}
|
|
222
|
+
|
|
223
|
+
admonitionPlugin.admonition = admonition
|
|
224
|
+
module.exports = admonitionPlugin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markdown-it-admon-collapsible",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.5",
|
|
4
4
|
"description": "Plugin to create admonitions for markdown-it markdown parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"admonition",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"clean": "git clean -fXd -e \\!node_modules -e \\!node_modules/**/*",
|
|
39
39
|
"coverage": "npm run test && c8 report --reporter html",
|
|
40
|
-
"lint": "eslint --fix .",
|
|
40
|
+
"lint": "eslint --fix . && markdownlint-cli2 **/*.md",
|
|
41
41
|
"test": "c8 mocha",
|
|
42
42
|
"build": "mkdir -p dist && node_modules/.bin/browserify ./ -s markdownitAdmonCollapsible > dist/markdown-it-admon-collapsible.js && terser dist/markdown-it-admon-collapsible.js -c -m > dist/markdown-it-admon-collapsible.min.js"
|
|
43
43
|
},
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
"eslint-plugin-promise": "^6.1.1",
|
|
52
52
|
"markdown-it": "^14.0.0",
|
|
53
53
|
"markdown-it-testgen": "~0.1.6",
|
|
54
|
+
"markdownlint": "^0.40.0",
|
|
55
|
+
"markdownlint-cli2": "^0.20.0",
|
|
56
|
+
"markdownlint-rule-numbered-headings-unique": "^1.0.7",
|
|
54
57
|
"mocha": "^11.7.5",
|
|
55
58
|
"rimraf": "^6.1.2",
|
|
56
59
|
"terser": "^5.26.0"
|