@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.
- package/README.md +20 -5
- package/index.js +38 -54
- package/package.json +2 -2
- package/src/phase0-description-list.js +232 -113
- package/src/phase1-analyze.js +39 -8
- package/src/phase2-convert.js +177 -193
- package/src/phase3-attributes.js +24 -47
- package/src/phase5-spans.js +73 -54
- package/src/preprocess-literal-lists.js +295 -362
- package/src/types-utility.js +0 -15
- package/src/phase6-attrs-migration.js +0 -184
package/src/phase3-attributes.js
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
35
|
+
if (hasAnyListItemValue) {
|
|
36
|
+
normalizeAndConvertValueAttributes(tokens, listCloseByOpen)
|
|
37
|
+
}
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
211
|
+
function normalizeAndConvertValueAttributes(tokens, listCloseByOpen = null) {
|
|
235
212
|
for (let i = 0; i < tokens.length; i++) {
|
|
236
213
|
const token = tokens[i]
|
|
237
214
|
|
package/src/phase5-spans.js
CHANGED
|
@@ -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,
|
|
12
|
+
export function generateSpans(tokens, opt) {
|
|
14
13
|
if (opt.useCounterStyle) {
|
|
15
14
|
return
|
|
16
15
|
}
|
|
17
|
-
const
|
|
18
|
-
const
|
|
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
|
|
30
|
-
if (!typeAttrs.type
|
|
31
|
-
addMarkerSpans(tokens, token, i, markerInfo, opt,
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|