@peaceroad/markdown-it-numbering-ul-regarded-as-ol 0.2.3 → 0.4.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.
@@ -7,72 +7,59 @@ import { buildListCloseIndexMap, findMatchingClose } from './list-helpers.js'
7
7
  /**
8
8
  * Add attributes to lists
9
9
  * @param {Array} tokens - Token array
10
- * @param {Array} listInfos - List information
11
10
  * @param {Object} opt - Options
12
11
  */
13
- export function addAttributes(tokens, listInfos, opt) {
14
- const listInfoMap = buildListInfoMap(listInfos)
12
+ export function addAttributes(tokens, opt) {
15
13
  const closeMap = buildListCloseIndexMap(tokens)
16
14
  const listCloseByOpen = closeMap.listCloseByOpen
15
+ let hasAnyListItemValue = false
17
16
 
18
17
  // Traverse token array and add attributes to ordered_list_open tokens
19
18
  // Token array may have been rebuilt in Phase2,
20
- // so don't use listInfo.startIndex, check token's _markerInfo/_listInfo
19
+ // so rely on token._markerInfo instead of index-based lookups.
21
20
  for (let i = 0; i < tokens.length; i++) {
22
21
  const token = tokens[i]
23
22
 
24
23
  if (token.type === 'ordered_list_open') {
25
- // Find listInfo corresponding to this token
26
- // Check _markerInfo or _convertedFromBullet flag saved in Phase2
27
- if (token._markerInfo || token._convertedFromBullet) {
28
- addListAttributesForToken(tokens, token, i, opt, null, listCloseByOpen)
29
- } else {
30
- // If originally ordered_list, find from listInfos
31
- const listInfo = listInfoMap.get(i)
32
- if (listInfo) {
33
- addListAttributesForToken(tokens, token, i, opt, listInfo, listCloseByOpen)
34
- }
35
- }
24
+ addListAttributesForToken(tokens, token, i, opt, listCloseByOpen)
25
+ continue
26
+ }
27
+ if (!hasAnyListItemValue && token.type === 'list_item_open' && hasValueAttr(token)) {
28
+ hasAnyListItemValue = true
36
29
  }
37
30
  }
38
31
 
39
32
  // Normalize value attributes of ordered_list generated by markdown-it
40
33
  // Remove unnecessary value attributes after Phase2 simplification
41
34
  // Normalize value attributes and marker metadata for all ordered lists
42
- normalizeAndConvertValueAttributes(tokens, opt, listCloseByOpen)
35
+ if (hasAnyListItemValue) {
36
+ normalizeAndConvertValueAttributes(tokens, listCloseByOpen)
37
+ }
43
38
  }
44
39
 
45
- /**
46
- * Build a lookup map for listInfos keyed by startIndex.
47
- */
48
- function buildListInfoMap(listInfos) {
49
- if (!Array.isArray(listInfos) || listInfos.length === 0) {
50
- return new Map()
40
+ function hasValueAttr(token) {
41
+ if (!Array.isArray(token.attrs)) {
42
+ return false
51
43
  }
52
-
53
- const map = new Map()
54
- for (const info of listInfos) {
55
- if (!info || typeof info.startIndex !== 'number' || !info.markerInfo) {
56
- continue
57
- }
58
- if (!map.has(info.startIndex)) {
59
- map.set(info.startIndex, info)
44
+ for (const [name] of token.attrs) {
45
+ if (name === 'value') {
46
+ return true
60
47
  }
61
48
  }
62
- return map
49
+ return false
63
50
  }
64
51
 
65
52
  /**
66
53
  * Add attributes to a single list token
67
54
  */
68
- function addListAttributesForToken(tokens, token, tokenIndex, opt, listInfo = null, listCloseByOpen = null) {
55
+ function addListAttributesForToken(tokens, token, tokenIndex, opt, listCloseByOpen = null) {
69
56
  // Initialize attribute array
70
57
  if (!token.attrs) {
71
58
  token.attrs = []
72
59
  }
73
60
 
74
61
  // Get marker info
75
- const markerInfo = token._markerInfo || listInfo?.markerInfo
62
+ const markerInfo = token._markerInfo
76
63
 
77
64
  if (!markerInfo) {
78
65
  // Default attributes for lists without markerInfo
@@ -107,13 +94,10 @@ function addListAttributesForToken(tokens, token, tokenIndex, opt, listInfo = nu
107
94
 
108
95
  // 1. type attribute or role attribute (add first)
109
96
  // Always use role="list" for alwaysMarkerSpan
110
- if (opt.useCounterStyle) {
111
- // When user chooses @counter-style, we avoid role and inline styles.
112
- // Still add class so users can target with CSS (e.g., counter-reset/counter-increment or @counter-style usage).
113
- // Do not add type attribute (counter-style will handle visuals)
114
- } else if (typeAttrs.type && !opt.alwaysMarkerSpan) {
97
+ if (!opt.useCounterStyle && typeAttrs.type && !opt.alwaysMarkerSpan) {
115
98
  addAttr(token, 'type', typeAttrs.type)
116
- } else {
99
+ } else if (!opt.useCounterStyle) {
100
+ // In non-counter-style mode, custom markers (or alwaysMarkerSpan) use role=list.
117
101
  addAttr(token, 'role', 'list')
118
102
  if (opt.hasListStyleNone) {
119
103
  addAttr(token, 'style', 'list-style: none;')
@@ -149,12 +133,6 @@ function addListAttributesForToken(tokens, token, tokenIndex, opt, listInfo = nu
149
133
  addAttr(token, 'class', typeAttrs.class)
150
134
  }
151
135
  }
152
-
153
- // If user requested @counter-style, add a helper class to identify lists
154
- if (opt.useCounterStyle) {
155
- // Do not add helper class when using counter-style; user CSS should target generated ol-* classes.
156
- }
157
-
158
136
  // 4. data-marker-prefix/suffix
159
137
  if (!opt.omitMarkerMetadata) {
160
138
  if (markerInfo.markers[0].prefix) {
@@ -229,9 +207,8 @@ function addListItemValues(tokens, listOpenIndex, markerInfo, listCloseByOpen =
229
207
  * Normalize value attributes generated by markdown-it for ordered lists.
230
208
  * Remove value attributes for consecutive numbers.
231
209
  * @param {Array} tokens - Token array
232
- * @param {Object} opt - Plugin options
233
210
  */
234
- function normalizeAndConvertValueAttributes(tokens, opt, listCloseByOpen = null) {
211
+ function normalizeAndConvertValueAttributes(tokens, listCloseByOpen = null) {
235
212
  for (let i = 0; i < tokens.length; i++) {
236
213
  const token = tokens[i]
237
214
 
@@ -7,15 +7,24 @@ import { buildListCloseIndexMap, findMatchingClose } from './list-helpers.js'
7
7
  /**
8
8
  * Generate marker spans
9
9
  * @param {Array} tokens - Token array
10
- * @param {Array} listInfos - List information
11
10
  * @param {Object} opt - Options
12
11
  */
13
- export function generateSpans(tokens, listInfos, opt) {
12
+ export function generateSpans(tokens, opt) {
14
13
  if (opt.useCounterStyle) {
15
14
  return
16
15
  }
17
- const closeMap = buildListCloseIndexMap(tokens)
18
- const listCloseByOpen = closeMap.listCloseByOpen
16
+ const rawSpanClass = opt?.markerSpanClass
17
+ const normalizedSpanClass = typeof rawSpanClass === 'string'
18
+ ? rawSpanClass.trim()
19
+ : ''
20
+ const spanClass = normalizedSpanClass || 'li-num'
21
+ let listCloseByOpen = null
22
+ const getListCloseByOpen = () => {
23
+ if (!listCloseByOpen) {
24
+ listCloseByOpen = buildListCloseIndexMap(tokens).listCloseByOpen
25
+ }
26
+ return listCloseByOpen
27
+ }
19
28
 
20
29
  // Traverse token array and add spans to ordered_list_open tokens
21
30
  for (let i = 0; i < tokens.length; i++) {
@@ -23,12 +32,16 @@ export function generateSpans(tokens, listInfos, opt) {
23
32
 
24
33
  if (token.type === 'ordered_list_open' && token._markerInfo) {
25
34
  const markerInfo = token._markerInfo
35
+ if (opt.alwaysMarkerSpan) {
36
+ addMarkerSpans(tokens, token, i, markerInfo, opt, spanClass, getListCloseByOpen())
37
+ continue
38
+ }
26
39
  const firstMarker = markerInfo.markers[0]
27
40
  const typeAttrs = getTypeAttributes(markerInfo.type, firstMarker, opt)
28
41
 
29
- // Generate span if no type attribute (custom marker) or alwaysMarkerSpan mode
30
- if (!typeAttrs.type || opt.alwaysMarkerSpan) {
31
- addMarkerSpans(tokens, token, i, markerInfo, opt, listCloseByOpen)
42
+ // Generate span for custom marker lists.
43
+ if (!typeAttrs.type) {
44
+ addMarkerSpans(tokens, token, i, markerInfo, opt, spanClass, getListCloseByOpen())
32
45
  }
33
46
  }
34
47
  }
@@ -37,7 +50,7 @@ export function generateSpans(tokens, listInfos, opt) {
37
50
  /**
38
51
  * Add marker <span> to the first inline token of each list item.
39
52
  */
40
- function addMarkerSpans(tokens, listToken, listIndex, markerInfo, opt, listCloseByOpen = null) {
53
+ function addMarkerSpans(tokens, listToken, listIndex, markerInfo, opt, spanClass, listCloseByOpen = null) {
41
54
  // Find end position of this ordered_list
42
55
  let listCloseIndex = listCloseByOpen ? listCloseByOpen[listIndex] : -1
43
56
  if (typeof listCloseIndex !== 'number' || listCloseIndex === -1) {
@@ -60,55 +73,61 @@ function addMarkerSpans(tokens, listToken, listIndex, markerInfo, opt, listClose
60
73
  // Add span only to first inline token in list_item
61
74
  else if (token.type === 'inline' && inListItem && !listItemInlineFound) {
62
75
  const marker = markerInfo.markers[markerIndex]
63
- if (marker && marker.marker) {
64
- // Insert span_open, text, span_close before inline token
65
- const spanOpen = new tokens[i].constructor('span_open', 'span', 1)
66
- const spanClass = (opt && opt.markerSpanClass) ? String(opt.markerSpanClass) : 'li-num'
67
- spanOpen.attrSet('class', spanClass)
68
- spanOpen.attrSet('aria-hidden', 'true')
69
-
70
- const text = new tokens[i].constructor('text', '', 0)
71
-
72
- // Determine marker content
73
- // If marker.number exists, get correct symbol based on it
74
- let markerContent = marker.marker
75
-
76
- if (marker.number !== undefined && markerInfo.type) {
77
- // Get correct symbol if number is specified
78
- const correctSymbol = getSymbolForNumber(markerInfo.type, marker.number)
79
- if (correctSymbol) {
80
- // Build in prefix + correctSymbol + suffix format
81
- markerContent = (marker.prefix || '') + correctSymbol + (marker.suffix || '')
82
- }
83
- }
84
-
85
- // Include entire marker (prefix+number+suffix) for alwaysMarkerSpan mode
86
- if (!opt.alwaysMarkerSpan) {
87
- // Normal mode: remove suffix for custom markers
88
- if (marker.suffix && markerContent.endsWith(marker.suffix)) {
89
- markerContent = markerContent.slice(0, -marker.suffix.length)
90
- }
91
- }
92
- // Use markerContent as-is (prefix+symbol+suffix) for alwaysMarkerSpan
93
- text.content = markerContent
94
-
95
- const spanClose = new tokens[i].constructor('span_close', 'span', -1)
96
-
97
- // Initialize children if not exist
98
- if (!token.children) {
99
- token.children = []
76
+ if (!marker) {
77
+ listItemInlineFound = true
78
+ continue
79
+ }
80
+ // Insert span_open, text, span_close before inline token
81
+ const spanOpen = new tokens[i].constructor('span_open', 'span', 1)
82
+ spanOpen.attrSet('class', spanClass)
83
+ spanOpen.attrSet('aria-hidden', 'true')
84
+
85
+ const text = new tokens[i].constructor('text', '', 0)
86
+
87
+ // Determine marker content
88
+ // If marker.number exists, get correct symbol based on it
89
+ let markerContent = marker.marker || ''
90
+
91
+ if (marker.number !== undefined && markerInfo.type) {
92
+ // Get correct symbol if number is specified
93
+ const correctSymbol = getSymbolForNumber(markerInfo.type, marker.number)
94
+ if (correctSymbol) {
95
+ // Build in prefix + correctSymbol + suffix format
96
+ markerContent = (marker.prefix || '') + correctSymbol + (marker.suffix || '')
100
97
  }
101
-
102
- // Add span element at beginning of children
103
- token.children.unshift(spanOpen, text, spanClose)
104
-
105
- // Add space before content if exists
106
- if (token.content) {
107
- const spaceToken = new tokens[i].constructor('text', '', 0)
108
- spaceToken.content = ' '
109
- token.children.push(spaceToken)
98
+ }
99
+
100
+ if (!markerContent) {
101
+ listItemInlineFound = true
102
+ continue
103
+ }
104
+
105
+ // Include entire marker (prefix+number+suffix) for alwaysMarkerSpan mode
106
+ if (!opt.alwaysMarkerSpan) {
107
+ // Normal mode: remove suffix for custom markers
108
+ if (marker.suffix && markerContent.endsWith(marker.suffix)) {
109
+ markerContent = markerContent.slice(0, -marker.suffix.length)
110
110
  }
111
111
  }
112
+ // Use markerContent as-is (prefix+symbol+suffix) for alwaysMarkerSpan
113
+ text.content = markerContent
114
+
115
+ const spanClose = new tokens[i].constructor('span_close', 'span', -1)
116
+
117
+ // Initialize children if not exist
118
+ if (!token.children) {
119
+ token.children = []
120
+ }
121
+
122
+ // Add span element at beginning of children
123
+ token.children.unshift(spanOpen, text, spanClose)
124
+
125
+ // Add space before content if exists
126
+ if (token.content) {
127
+ const spaceToken = new tokens[i].constructor('text', '', 0)
128
+ spaceToken.content = ' '
129
+ token.children.push(spaceToken)
130
+ }
112
131
  listItemInlineFound = true // First inline of this list_item is processed
113
132
  }
114
133
  // When list_item_close is found, go to next list_item