@peaceroad/markdown-it-footnote-here 0.2.1 → 0.3.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.
Files changed (4) hide show
  1. package/AGENTS.md +33 -0
  2. package/README.md +35 -1
  3. package/index.js +174 -58
  4. package/package.json +1 -1
package/AGENTS.md ADDED
@@ -0,0 +1,33 @@
1
+ # Workflow for Updating markdown-it-footnote-here
2
+
3
+ This document captures the current implementation workflow, especially around footnotes/endnotes.
4
+
5
+ ## Code overview
6
+ - `index.js`:
7
+ - Rendering helpers: `render_footnote_ref`, `render_footnote_open/close`, `render_footnote_anchor`, and `createAfterBackLinkToken`.
8
+ - Parsing: custom block rule `footnote_def`, inline rule `footnote_ref`, core rule `footnote_anchor`, and `endnotes_move` to append endnotes at the end.
9
+ - Endnote handling: labels starting with `endnotesPrefix` (default `en-`) are endnotes; DOM ids/classes use fixed `en`.
10
+ - Options include backlink placement, label customization, and endnotes section attributes.
11
+
12
+ ## Adding features / making changes
13
+ 1) Review options and defaults in `index.js` (`opt` object).
14
+ 2) Keep DOM prefixes stable:
15
+ - Footnotes use `fn`; endnotes use `en` for ids/classes (`en1`, `en-ref1`, etc.).
16
+ 3) Parsing flow:
17
+ - `footnote_def` registers notes into `env.footnotes` or `env.endnotes` based on `endnotesPrefix`.
18
+ - `footnote_ref` resolves references via `selectNoteEnv` and tags tokens with `isEndnote`.
19
+ 4) Rendering flow:
20
+ - `footnote_anchor` injects backlinks/labels into footnote content.
21
+ - `endnotes_move` removes endnote blocks from inline positions and appends a `<section>` (attributes ordered aria-label → id → class → role).
22
+ 5) When adding options:
23
+ - Update `README.md` and add fixtures under `test/`.
24
+ - Extend `test/test.js` to load the new fixtures.
25
+
26
+ ## Testing
27
+ - Run `npm test` (uses `test/test.js` and fixtures under `test/`).
28
+ - Fixtures format: alternating `[Markdown]` and `[HTML]` blocks.
29
+
30
+ ## Notes
31
+ - `labelSupTag` applies to both footnotes and endnotes.
32
+ - If `endnotesPrefix` is empty, endnotes are disabled.
33
+ - `endnotesUseHeading` true: render `<h2>` with `endnotesSectionAriaLabel`; false: use `aria-label` without heading.
package/README.md CHANGED
@@ -28,6 +28,34 @@ HTML:
28
28
 
29
29
  Notice. When multiple instances of the same footnote number appear in the main content, the default behavior is that the backlink from the footnote will refer to the first instance.
30
30
 
31
+ ## Endnotes
32
+
33
+ When a footnote label starts with the endnote prefix (default: `en-`), it is collected at the end of the document and rendered as endnotes. The reference/backlink label for endnotes is prefixed by `endnotesLabelPrefix` (default: `E`), so endnotes appear as `[E1]`, `[E2]`, ...
34
+
35
+ Markdown:
36
+
37
+ ```md
38
+ A paragraph.[^en-1]
39
+
40
+ [^en-1]: A endnote.
41
+
42
+ A paragraph.
43
+ ```
44
+
45
+ HTML:
46
+
47
+ ```html
48
+ <p>A paragraph.<a href="#en1" id="en-ref1" class="en-noteref" role="doc-noteref">[E1]</a></p>
49
+ <p>A paragraph.</p>
50
+ <section aria-label="Notes" id="endnotes" role="doc-endnotes">
51
+ <ol>
52
+ <li id="en1">
53
+ <p><a href="#en-ref1" class="en-backlink" role="doc-backlink">[E1]</a> A endnote.</p>
54
+ </li>
55
+ </ol>
56
+ </section>
57
+ ```
58
+
31
59
  ## Use
32
60
 
33
61
  ```js
@@ -53,7 +81,13 @@ npm install @peaceroad/markdown-it-footnote-here
53
81
  - afterBacklinkSuffixArabicNumerals (boolean): If true, backlink suffix uses numbers (1, 2, ...) instead of letters (a, b, ...).
54
82
  - afterBacklinkdAriaLabelPrefix (string): Prefix for aria-label of backlink (default: 'Back to reference ').
55
83
  - labelBra (string): Bracket to use before footnote number (default: '[').
56
- - labelKet (string): Bracket to use after footnote number (default: ']').
84
+ - labelKet (string): Bracket to use after footnote number (default: ']').
57
85
  - labelSupTag (boolean): If true, wraps footnote reference in `<sup>` tag.
58
86
  - backLabelBra (string): Bracket to use before backlink number (default: '[').
59
87
  - backLabelKet (string): Bracket to use after backlink number (default: ']').
88
+ - endnotesPrefix (string): Prefix that marks a footnote as an endnote (default: `'en-'`).
89
+ - endnotesLabelPrefix (string): Label prefix for endnote refs/backlinks (default: `'E'`, e.g., `[E1]`).
90
+ - endnotesSectionId (string): `id` attribute for the endnotes section wrapper; omitted when empty (default: `'endnotes'`).
91
+ - endnotesSectionClass (string): `class` attribute for the endnotes section wrapper; omitted when empty (default: `''`).
92
+ - endnotesSectionAriaLabel (string): Used as `aria-label` when `endnotesUseHeading` is false. When `endnotesUseHeading` is true, this value becomes the heading text (default: `'Notes'`).
93
+ - endnotesUseHeading (boolean): If true, render `<h2>{endnotesSectionAriaLabel}</h2>` and omit `aria-label`. If false (default), omit the heading and set `aria-label` when provided.
package/index.js CHANGED
@@ -8,71 +8,124 @@ const render_footnote_anchor_name = (tokens, idx, opt, env) => {
8
8
  return prefix + n
9
9
  }
10
10
 
11
+ const isEndnoteLabel = (label, opt) => {
12
+ if (!opt.endnotesPrefix) return false
13
+ return label.startsWith(opt.endnotesPrefix)
14
+ }
15
+
16
+ const ensureNotesEnv = (env, key) => {
17
+ if (!env[key]) {
18
+ env[key] = { length: 0, refs: {}, positions: [] }
19
+ }
20
+ return env[key]
21
+ }
22
+
23
+ const ENDNOTE_DOM_PREFIX = 'en'
24
+
25
+ const getDomPrefix = (isEndnote) => (isEndnote ? ENDNOTE_DOM_PREFIX : 'fn')
26
+ const getDisplayPrefix = (isEndnote, opt) => (isEndnote ? opt.endnotesLabelPrefix : '')
27
+
28
+ const getNotesMeta = (token, env) => {
29
+ if (token.meta && token.meta.isEndnote) return env.endnotes
30
+ return env.footnotes
31
+ }
32
+
33
+ const selectNoteEnv = (label, env, preferEndnote) => {
34
+ const endRefs = env.endnotes && env.endnotes.refs
35
+ const footRefs = env.footnotes && env.footnotes.refs
36
+ const endId = endRefs && endRefs[':' + label]
37
+ const footId = footRefs && footRefs[':' + label]
38
+
39
+ if (preferEndnote && endId !== undefined) {
40
+ return { env: env.endnotes, id: endId, isEndnote: true }
41
+ }
42
+ if (footId !== undefined) {
43
+ return { env: env.footnotes, id: footId, isEndnote: false }
44
+ }
45
+ if (endId !== undefined) {
46
+ return { env: env.endnotes, id: endId, isEndnote: true }
47
+ }
48
+ return null
49
+ }
50
+
11
51
  const render_footnote_ref = (tokens, idx, opt, env) => {
12
52
  const token = tokens[idx]
13
53
  const id = token.meta.id
14
54
  const n = id + 1
15
- const footnotes = env.footnotes
16
- footnotes._refCount = footnotes._refCount || {}
17
- let refIdx = (footnotes._refCount[id] = (footnotes._refCount[id] || 0) + 1)
55
+ const notes = getNotesMeta(token, env)
56
+ const isEndnote = !!token.meta.isEndnote
57
+ const noteDomPrefix = getDomPrefix(isEndnote)
58
+ const displayPrefix = getDisplayPrefix(isEndnote, opt)
59
+ notes._refCount = notes._refCount || {}
60
+ let refIdx = (notes._refCount[id] = (notes._refCount[id] || 0) + 1)
18
61
  if (!opt.afterBacklinkSuffixArabicNumerals) {
19
62
  refIdx = String.fromCharCode(96 + refIdx)
20
63
  }
21
64
  let suffix = ''
22
- let label = `${opt.labelBra}${n}${opt.labelKet}`
23
- if (footnotes.totalCounts && footnotes.totalCounts[id] > 1) {
65
+ let label = `${opt.labelBra}${displayPrefix}${n}${opt.labelKet}`
66
+ if (notes.totalCounts && notes.totalCounts[id] > 1) {
24
67
  suffix = '-' + refIdx
25
68
  if (opt.beforeSameBacklink) {
26
- label = `${opt.labelBra}${n}${suffix}${opt.labelKet}`
69
+ label = `${opt.labelBra}${displayPrefix}${n}${suffix}${opt.labelKet}`
27
70
  }
28
71
  }
29
- let refCont = `<a href="#fn${n}" id="fn-ref${n}${suffix}" class="fn-noteref" role="doc-noteref">${label}</a>`
30
- if (opt.labelSupTag) refCont = `<sup class="fn-noteref-wrapper">${refCont}</sup>`
72
+ const href = `${noteDomPrefix}${n}`
73
+ let refCont = `<a href="#${href}" id="${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-noteref" role="doc-noteref">${label}</a>`
74
+ if (opt.labelSupTag) refCont = `<sup class="${noteDomPrefix}-noteref-wrapper">${refCont}</sup>`
31
75
  return refCont
32
76
  }
33
77
 
34
78
  const render_footnote_open = (tokens, idx, opt, env, slf) => {
35
79
  const id = slf.rules.footnote_anchor_name(tokens, idx, opt, env, slf)
80
+ const isEndnote = tokens[idx].meta && tokens[idx].meta.isEndnote
81
+ if (isEndnote) return `<li id="${ENDNOTE_DOM_PREFIX}${id}">\n`
36
82
  return `<aside id="fn${id}" class="fn" role="doc-footnote">\n`
37
83
  }
38
84
 
39
- const render_footnote_close = () => {
85
+ const render_footnote_close = (tokens, idx) => {
86
+ const isEndnote = tokens[idx].meta && tokens[idx].meta.isEndnote
87
+ if (isEndnote) return `</li>\n`
40
88
  return `</aside>\n`
41
89
  }
42
90
 
43
91
  const render_footnote_anchor = (tokens, idx, opt, env) => {
44
92
  const idNum = tokens[idx].meta.id
45
93
  const n = idNum + 1
46
- const footnotes = env.footnotes
47
- const counts = footnotes && footnotes.totalCounts
94
+ const isEndnote = tokens[idx].meta && tokens[idx].meta.isEndnote
95
+ const notes = getNotesMeta(tokens[idx], env)
96
+ const counts = notes && notes.totalCounts
97
+ const noteDomPrefix = getDomPrefix(!!isEndnote)
98
+ const displayPrefix = getDisplayPrefix(!!isEndnote, opt)
99
+
48
100
  if (opt.beforeSameBacklink && counts && counts[idNum] > 1) {
49
101
  let links = ''
50
102
  for (let i = 1; i <= counts[idNum]; i++) {
51
103
  const suffix = '-' + String.fromCharCode(96 + i); // a, b, c ...
52
- links += `<a href="#fn-ref${n}${suffix}" class="fn-backlink" role="doc-backlink">${opt.backLabelBra}${n}${suffix}${opt.backLabelKet}</a>`
104
+ links += `<a href="#${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${suffix}${opt.backLabelKet}</a>`
53
105
  }
54
106
  return links + ' '
55
107
  }
56
108
 
57
109
  if (opt.afterBacklink) {
58
- return `<span class="fn-label">${opt.backLabelBra}${n}${opt.backLabelKet}</span> `
110
+ return `<span class="${noteDomPrefix}-label">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</span> `
59
111
  }
60
112
 
61
113
  if (counts && counts[idNum] > 1) {
62
- return `<a href="#fn-ref${n}-a" class="fn-backlink" role="doc-backlink">${opt.backLabelBra}${n}${opt.backLabelKet}</a> `
114
+ return `<a href="#${noteDomPrefix}-ref${n}-a" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</a> `
63
115
  }
64
116
 
65
- return `<a href="#fn-ref${n}" class="fn-backlink" role="doc-backlink">${opt.backLabelBra}${n}${opt.backLabelKet}</a> `
117
+ return `<a href="#${noteDomPrefix}-ref${n}" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</a> `
66
118
  }
67
119
 
68
- function createAfterBackLinkToken(state, counts, n, opt) {
120
+ function createAfterBackLinkToken(state, counts, n, opt, noteDomPrefix, isEndnote) {
121
+ const displayPrefix = isEndnote ? opt.endnotesLabelPrefix : ''
69
122
  let html = ' '
70
123
  if (counts && counts > 1) {
71
124
  for (let i = 1; i <= counts; i++) {
72
125
  const suffixChar = opt.afterBacklinkSuffixArabicNumerals ? i : String.fromCharCode(96 + i)
73
126
  const suffix = '-' + suffixChar
74
- html += `<a href="#fn-ref${n}${suffix}" class="fn-backlink" role="doc-backlink"`
75
- if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${n}${suffix}"`
127
+ html += `<a href="#${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-backlink" role="doc-backlink"`
128
+ if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${displayPrefix}${n}${suffix}"`
76
129
  html += `>${opt.afterBacklinkContent}`
77
130
  if (opt.afterBacklinkWithNumber) {
78
131
  html += `<sup>${suffixChar}</sup>`
@@ -80,8 +133,8 @@ function createAfterBackLinkToken(state, counts, n, opt) {
80
133
  html += `</a>`
81
134
  }
82
135
  } else {
83
- html += `<a href="#fn-ref${n}" class="fn-backlink" role="doc-backlink"`
84
- if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${n}"`
136
+ html += `<a href="#${noteDomPrefix}-ref${n}" class="${noteDomPrefix}-backlink" role="doc-backlink"`
137
+ if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${displayPrefix}${n}"`
85
138
  html += `>${opt.afterBacklinkContent}</a>`
86
139
  }
87
140
  const token = new state.Token('html_inline', '', 0)
@@ -102,16 +155,22 @@ const footnote_plugin = (md, option) =>{
102
155
  afterBacklinkWithNumber: false,
103
156
  afterBacklinkSuffixArabicNumerals: false,
104
157
  afterBacklinkdAriaLabelPrefix: 'Back to reference ', /* 戻る:本文参照 */
158
+ endnotesPrefix: 'en-',
159
+ endnotesLabelPrefix: 'E',
160
+ endnotesSectionId: 'endnotes',
161
+ endnotesSectionClass: '',
162
+ endnotesSectionAriaLabel: 'Notes',
163
+ endnotesUseHeading: false,
105
164
  }
106
165
  if (option) Object.assign(opt, option)
107
166
 
108
167
  const isSpace = md.utils.isSpace
109
168
 
110
169
  md.renderer.rules.footnote_ref = (tokens, idx, _options, env) => render_footnote_ref(tokens, idx, opt, env)
111
- md.renderer.rules.footnote_open = render_footnote_open
112
- md.renderer.rules.footnote_close = render_footnote_close
170
+ md.renderer.rules.footnote_open = (tokens, idx, _options, env, slf) => render_footnote_open(tokens, idx, opt, env, slf)
171
+ md.renderer.rules.footnote_close = (tokens, idx, _options, env, slf) => render_footnote_close(tokens, idx, opt, env, slf)
113
172
  md.renderer.rules.footnote_anchor = (tokens, idx, _options, env, slf) => render_footnote_anchor(tokens, idx, opt, env, slf)
114
- md.renderer.rules.footnote_anchor_name = render_footnote_anchor_name
173
+ md.renderer.rules.footnote_anchor_name = (tokens, idx, _options, env, slf) => render_footnote_anchor_name(tokens, idx, opt, env, slf)
115
174
 
116
175
  // Process footnote block definition
117
176
  const footnote_def = (state, startLine, endLine, silent) => {
@@ -134,16 +193,13 @@ const footnote_plugin = (md, option) =>{
134
193
 
135
194
  if (silent) { return true; }
136
195
 
137
- // initialize footnotes environment once
138
- if (!state.env.footnotes) {
139
- state.env.footnotes = { length: 0, refs: {}, positions: [] }
140
- }
141
- const fn = state.env.footnotes
196
+ const isEndnote = isEndnoteLabel(label, opt)
197
+ const fn = ensureNotesEnv(state.env, isEndnote ? 'endnotes' : 'footnotes')
142
198
  const id = fn.length++
143
199
  fn.refs[':' + label] = id
144
200
 
145
201
  const token = new state.Token('footnote_open', '', 1)
146
- token.meta = { id, label }
202
+ token.meta = { id, label, isEndnote }
147
203
  token.level = state.level++
148
204
  state.tokens.push(token)
149
205
  fn.positions.push(state.tokens.length - 1)
@@ -193,6 +249,7 @@ const footnote_plugin = (md, option) =>{
193
249
 
194
250
  const closeToken = new state.Token('footnote_close', '', -1)
195
251
  closeToken.level = --state.level
252
+ closeToken.meta = { isEndnote }
196
253
  state.tokens.push(closeToken)
197
254
 
198
255
  return true
@@ -208,9 +265,6 @@ const footnote_plugin = (md, option) =>{
208
265
  return false
209
266
  }
210
267
 
211
- const env = state.env
212
- if (!env.footnotes || !env.footnotes.refs) { return false; }
213
-
214
268
  let pos = start + 2
215
269
  let found = false
216
270
  for (; pos < posMax && !found; pos++) {
@@ -223,12 +277,13 @@ const footnote_plugin = (md, option) =>{
223
277
  pos++; // pos set next ']' position.
224
278
 
225
279
  const label = src.slice(start + 2, pos - 1)
226
- const id = env.footnotes.refs[':' + label]
227
-
228
- if (id === undefined) { return false; }
280
+ const env = state.env
281
+ const preferEndnote = isEndnoteLabel(label, opt)
282
+ const resolved = selectNoteEnv(label, env, preferEndnote)
229
283
 
284
+ if (!resolved) { return false; }
230
285
  if (!silent) {
231
- const fn = env.footnotes
286
+ const fn = resolved.env
232
287
 
233
288
  if (!fn.list) { fn.list = []; }
234
289
 
@@ -236,10 +291,10 @@ const footnote_plugin = (md, option) =>{
236
291
  fn.list[footnoteId] = { label, count: 0 }
237
292
 
238
293
  fn.totalCounts = fn.totalCounts || {}
239
- fn.totalCounts[id] = (fn.totalCounts[id] || 0) + 1
294
+ fn.totalCounts[resolved.id] = (fn.totalCounts[resolved.id] || 0) + 1
240
295
 
241
296
  const token = state.push('footnote_ref', '', 0)
242
- token.meta = { id, label }
297
+ token.meta = { id: resolved.id, label, isEndnote: resolved.isEndnote }
243
298
  }
244
299
 
245
300
  state.pos = pos
@@ -247,42 +302,103 @@ const footnote_plugin = (md, option) =>{
247
302
  }
248
303
 
249
304
  const footnote_anchor = (state) => {
305
+ if (!state.env.footnotes && !state.env.endnotes) return
250
306
  const tokens = state.tokens
251
- const fn = state.env.footnotes
252
- const positions = fn && fn.positions
253
- if (!positions || positions.length === 0) { return; }
254
-
255
- const createAnchorToken = (id) => {
307
+ const createAnchorToken = (id, isEndnote) => {
256
308
  const aToken = new state.Token('footnote_anchor', '', 0)
257
- aToken.meta = { id, label: id + 1 }
309
+ aToken.meta = { id, label: id + 1, isEndnote }
258
310
  return aToken
259
311
  }
260
312
 
261
- for (let j = 0, len = positions.length; j < len; ++j) {
262
- const posOpen = positions[j]
263
- if (posOpen + 2 >= tokens.length) continue
313
+ const injectAnchors = (notes, isEndnote) => {
314
+ const positions = notes && notes.positions
315
+ if (!positions || positions.length === 0) { return; }
316
+
317
+ for (let j = 0, len = positions.length; j < len; ++j) {
318
+ const posOpen = positions[j]
319
+ if (posOpen + 2 >= tokens.length) continue
264
320
 
265
- const t1 = tokens[posOpen + 1]
266
- if (t1.type !== 'paragraph_open') continue
321
+ const t1 = tokens[posOpen + 1]
322
+ if (t1.type !== 'paragraph_open') continue
267
323
 
268
- const t2 = tokens[posOpen + 2]
269
- if (t2.type !== 'inline') continue
324
+ const t2 = tokens[posOpen + 2]
325
+ if (t2.type !== 'inline') continue
270
326
 
271
- const t0 = tokens[posOpen]
272
- const id = t0.meta.id
327
+ const t0 = tokens[posOpen]
328
+ const id = t0.meta.id
329
+ const noteDomPrefix = isEndnote ? ENDNOTE_DOM_PREFIX : 'fn'
273
330
 
274
- t2.children.unshift(createAnchorToken(id))
275
- if (opt.afterBacklink) {
276
- const n = id + 1
277
- const counts = fn.totalCounts && fn.totalCounts[id]
278
- t2.children.push(createAfterBackLinkToken(state, counts, n, opt))
331
+ t2.children.unshift(createAnchorToken(id, isEndnote))
332
+ if (opt.afterBacklink) {
333
+ const n = id + 1
334
+ const counts = notes.totalCounts && notes.totalCounts[id]
335
+ t2.children.push(createAfterBackLinkToken(state, counts, n, opt, noteDomPrefix, isEndnote))
336
+ }
279
337
  }
280
338
  }
339
+
340
+ injectAnchors(state.env.footnotes, false)
341
+ injectAnchors(state.env.endnotes, true)
342
+ }
343
+
344
+ const move_endnotes_to_section = (state) => {
345
+ if (!opt.endnotesPrefix) return
346
+ if (!state.env.endnotes || !state.env.endnotes.positions || state.env.endnotes.positions.length === 0) {
347
+ return
348
+ }
349
+
350
+ const tokens = state.tokens
351
+ const endnoteBlocks = []
352
+
353
+ for (let i = 0; i < tokens.length; i++) {
354
+ const token = tokens[i]
355
+ if (token.type !== 'footnote_open' || !token.meta || !token.meta.isEndnote) continue
356
+
357
+ let j = i + 1
358
+ while (j < tokens.length) {
359
+ const t = tokens[j]
360
+ if (t.type === 'footnote_close' && t.meta && t.meta.isEndnote) {
361
+ j++
362
+ break
363
+ }
364
+ j++
365
+ }
366
+ endnoteBlocks.push(tokens.slice(i, j))
367
+ tokens.splice(i, j - i)
368
+ i--
369
+ }
370
+
371
+ if (endnoteBlocks.length === 0) return
372
+
373
+ const sectionOpen = new state.Token('html_block', '', 0)
374
+ const attrs = []
375
+ if (!opt.endnotesUseHeading && opt.endnotesSectionAriaLabel) {
376
+ attrs.push(`aria-label="${opt.endnotesSectionAriaLabel}"`)
377
+ }
378
+ if (opt.endnotesSectionId) attrs.push(`id="${opt.endnotesSectionId}"`)
379
+ if (opt.endnotesSectionClass) attrs.push(`class="${opt.endnotesSectionClass}"`)
380
+ attrs.push('role="doc-endnotes"')
381
+ let sectionContent = `<section ${attrs.join(' ')}>\n`
382
+ if (opt.endnotesUseHeading && opt.endnotesSectionAriaLabel) {
383
+ sectionContent += `<h2>${opt.endnotesSectionAriaLabel}</h2>\n`
384
+ }
385
+ sectionContent += '<ol>\n'
386
+ sectionOpen.content = sectionContent
387
+
388
+ const sectionClose = new state.Token('html_block', '', 0)
389
+ sectionClose.content = '</ol>\n</section>\n'
390
+
391
+ tokens.push(sectionOpen)
392
+ endnoteBlocks.forEach(block => {
393
+ tokens.push(...block)
394
+ })
395
+ tokens.push(sectionClose)
281
396
  }
282
397
 
283
398
  md.block.ruler.before('reference', 'footnote_def', footnote_def, { alt: [ 'paragraph', 'reference' ] })
284
399
  md.inline.ruler.after('image', 'footnote_ref', footnote_ref)
285
400
  md.core.ruler.after('inline', 'footnote_anchor', footnote_anchor)
401
+ md.core.ruler.after('footnote_anchor', 'endnotes_move', move_endnotes_to_section)
286
402
  }
287
403
 
288
404
  export default footnote_plugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peaceroad/markdown-it-footnote-here",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "A markdown-it plugin. This generate aside[role|doc-footnote] element just below the footnote reference paragraph.",
5
5
  "main": "index.js",
6
6
  "type":"module",