@peaceroad/markdown-it-strong-ja 0.7.2 → 0.8.1
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 +326 -195
- package/index.js +27 -40
- package/package.json +26 -6
- package/src/token-compat.js +71 -22
- package/src/token-core.js +521 -132
- package/src/token-link-utils.js +434 -539
- package/src/token-postprocess/broken-ref.js +475 -0
- package/src/token-postprocess/fastpaths.js +349 -0
- package/src/token-postprocess/guards.js +499 -0
- package/src/token-postprocess/orchestrator.js +672 -0
- package/src/token-postprocess.js +1 -334
- package/src/token-utils.js +215 -142
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import Token from 'markdown-it/lib/token.mjs'
|
|
2
|
+
|
|
3
|
+
const cloneMap = (map) => {
|
|
4
|
+
if (!map || !Array.isArray(map)) return null
|
|
5
|
+
return [map[0], map[1]]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const cloneTextLike = (source, content) => {
|
|
9
|
+
const token = new Token('text', '', 0)
|
|
10
|
+
Object.assign(token, source)
|
|
11
|
+
token.content = content
|
|
12
|
+
if (source.meta) token.meta = { ...source.meta }
|
|
13
|
+
return token
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const toStrongToken = (token, isOpen) => {
|
|
17
|
+
if (!token) return
|
|
18
|
+
token.type = isOpen ? 'strong_open' : 'strong_close'
|
|
19
|
+
token.tag = 'strong'
|
|
20
|
+
token.nesting = isOpen ? 1 : -1
|
|
21
|
+
token.markup = '**'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const createStrongBoundaryToken = (isOpen, mapToken) => {
|
|
25
|
+
const token = new Token(isOpen ? 'strong_open' : 'strong_close', 'strong', isOpen ? 1 : -1)
|
|
26
|
+
token.markup = '**'
|
|
27
|
+
const map = mapToken && mapToken.map ? cloneMap(mapToken.map) : null
|
|
28
|
+
if (map) token.map = map
|
|
29
|
+
return token
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isTextToken = (token, requireContent = false) => {
|
|
33
|
+
if (!token || token.type !== 'text') return false
|
|
34
|
+
return !requireContent || !!token.content
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const tryFixTailPatternTokenOnly = (tokens, startIdx, endIdx) => {
|
|
38
|
+
if (!tokens || startIdx < 0 || endIdx < startIdx) return false
|
|
39
|
+
if ((endIdx - startIdx) < 14) return false
|
|
40
|
+
|
|
41
|
+
const t0 = tokens[startIdx]
|
|
42
|
+
const t1 = tokens[startIdx + 1]
|
|
43
|
+
const t2 = tokens[startIdx + 2]
|
|
44
|
+
const t3 = tokens[startIdx + 3]
|
|
45
|
+
const t4 = tokens[startIdx + 4]
|
|
46
|
+
const t5 = tokens[startIdx + 5]
|
|
47
|
+
const t6 = tokens[startIdx + 6]
|
|
48
|
+
const t7 = tokens[startIdx + 7]
|
|
49
|
+
const t8 = tokens[startIdx + 8]
|
|
50
|
+
const t9 = tokens[startIdx + 9]
|
|
51
|
+
const t10 = tokens[startIdx + 10]
|
|
52
|
+
const t11 = tokens[startIdx + 11]
|
|
53
|
+
const t12 = tokens[startIdx + 12]
|
|
54
|
+
const t13 = tokens[startIdx + 13]
|
|
55
|
+
const t14 = tokens[startIdx + 14]
|
|
56
|
+
|
|
57
|
+
if (!isTextToken(t0)) return false
|
|
58
|
+
if (!t1 || t1.type !== 'strong_close') return false
|
|
59
|
+
if (!isTextToken(t2, true)) return false
|
|
60
|
+
if (!t3 || t3.type !== 'em_open') return false
|
|
61
|
+
if (!isTextToken(t4)) return false
|
|
62
|
+
if (!t5 || t5.type !== 'strong_open') return false
|
|
63
|
+
if (!isTextToken(t6, true)) return false
|
|
64
|
+
if (!t7 || t7.type !== 'strong_close') return false
|
|
65
|
+
if (!isTextToken(t8)) return false
|
|
66
|
+
if (!t9 || t9.type !== 'em_close') return false
|
|
67
|
+
if (!isTextToken(t10, true)) return false
|
|
68
|
+
if (!t11 || t11.type !== 'em_open') return false
|
|
69
|
+
if (!isTextToken(t12, true)) return false
|
|
70
|
+
if (!t13 || t13.type !== 'em_close') return false
|
|
71
|
+
if (!isTextToken(t14, true)) return false
|
|
72
|
+
|
|
73
|
+
const splitPos = t14.content.indexOf('**')
|
|
74
|
+
if (splitPos <= 0) return false
|
|
75
|
+
const beforeTailMarker = t14.content.slice(0, splitPos)
|
|
76
|
+
const afterTailMarker = t14.content.slice(splitPos + 2)
|
|
77
|
+
if (!beforeTailMarker) return false
|
|
78
|
+
|
|
79
|
+
// Keep this fast path scoped to local malformed emphasis tails only.
|
|
80
|
+
for (let i = startIdx; i <= startIdx + 14 && i <= endIdx; i++) {
|
|
81
|
+
const token = tokens[i]
|
|
82
|
+
if (!token) continue
|
|
83
|
+
if (token.type === 'link_open' || token.type === 'link_close') return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
toStrongToken(t1, true)
|
|
87
|
+
t14.content = beforeTailMarker
|
|
88
|
+
|
|
89
|
+
const replacement = [
|
|
90
|
+
t0,
|
|
91
|
+
t1,
|
|
92
|
+
t2,
|
|
93
|
+
createStrongBoundaryToken(false, t2),
|
|
94
|
+
t3,
|
|
95
|
+
t4,
|
|
96
|
+
t6,
|
|
97
|
+
t8,
|
|
98
|
+
t9,
|
|
99
|
+
createStrongBoundaryToken(true, t10),
|
|
100
|
+
t10,
|
|
101
|
+
t11,
|
|
102
|
+
t12,
|
|
103
|
+
t13,
|
|
104
|
+
t14,
|
|
105
|
+
createStrongBoundaryToken(false, t14)
|
|
106
|
+
]
|
|
107
|
+
if (afterTailMarker) replacement.push(cloneTextLike(t14, afterTailMarker))
|
|
108
|
+
for (let i = startIdx + 15; i <= endIdx; i++) replacement.push(tokens[i])
|
|
109
|
+
|
|
110
|
+
tokens.splice(startIdx, endIdx - startIdx + 1, ...replacement)
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const tryFixTailDanglingStrongCloseTokenOnly = (tokens, startIdx, strongCloseIdx) => {
|
|
115
|
+
if (!tokens || startIdx < 0 || strongCloseIdx <= startIdx || strongCloseIdx >= tokens.length) return false
|
|
116
|
+
if (strongCloseIdx !== startIdx + 1) return false
|
|
117
|
+
const head = tokens[startIdx]
|
|
118
|
+
const close = tokens[strongCloseIdx]
|
|
119
|
+
if (!head || head.type !== 'text') return false
|
|
120
|
+
if (!close || close.type !== 'strong_close') return false
|
|
121
|
+
if (close.markup && close.markup !== '**') return false
|
|
122
|
+
|
|
123
|
+
const tail = tokens[strongCloseIdx + 1]
|
|
124
|
+
const closeMarkup = close.markup || '**'
|
|
125
|
+
const tailText = tail && tail.type === 'text' ? (tail.content || '') : ''
|
|
126
|
+
head.content = (head.content || '') + closeMarkup + tailText
|
|
127
|
+
|
|
128
|
+
if (tail && tail.type === 'text') {
|
|
129
|
+
tokens.splice(strongCloseIdx, 2)
|
|
130
|
+
} else {
|
|
131
|
+
tokens.splice(strongCloseIdx, 1)
|
|
132
|
+
}
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const tryFixBrokenRefStrongAroundLinkTokenOnly = (tokens, startIdx, endIdx, linkCloseMap) => {
|
|
137
|
+
if (!tokens || startIdx < 1 || endIdx <= startIdx + 6) return false
|
|
138
|
+
|
|
139
|
+
const outerOpen = tokens[startIdx - 1]
|
|
140
|
+
const headText = tokens[startIdx]
|
|
141
|
+
const earlyStrongClose = tokens[startIdx + 1]
|
|
142
|
+
const middleText = tokens[startIdx + 2]
|
|
143
|
+
const innerStrongOpen = tokens[startIdx + 3]
|
|
144
|
+
const linkOpen = tokens[startIdx + 4]
|
|
145
|
+
|
|
146
|
+
if (!outerOpen || outerOpen.type !== 'strong_open') return false
|
|
147
|
+
if (!headText || headText.type !== 'text' || !headText.content) return false
|
|
148
|
+
if (!earlyStrongClose || earlyStrongClose.type !== 'strong_close') return false
|
|
149
|
+
if (!middleText || middleText.type !== 'text' || !middleText.content) return false
|
|
150
|
+
if (!innerStrongOpen || innerStrongOpen.type !== 'strong_open') return false
|
|
151
|
+
if (!linkOpen || linkOpen.type !== 'link_open') return false
|
|
152
|
+
|
|
153
|
+
if ((earlyStrongClose.markup && earlyStrongClose.markup !== '**') ||
|
|
154
|
+
(innerStrongOpen.markup && innerStrongOpen.markup !== '**')) {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const linkOpenIdx = startIdx + 4
|
|
159
|
+
const closeIdx = linkCloseMap ? (linkCloseMap.get(linkOpenIdx) ?? -1) : -1
|
|
160
|
+
if (closeIdx === -1) return false
|
|
161
|
+
|
|
162
|
+
const tailTextIdx = closeIdx + 1
|
|
163
|
+
const tailStrongCloseIdx = closeIdx + 2
|
|
164
|
+
if (tailStrongCloseIdx !== endIdx) return false
|
|
165
|
+
|
|
166
|
+
const tailText = tokens[tailTextIdx]
|
|
167
|
+
const tailStrongClose = tokens[tailStrongCloseIdx]
|
|
168
|
+
if (!tailText || tailText.type !== 'text') return false
|
|
169
|
+
if (!tailStrongClose || tailStrongClose.type !== 'strong_close') return false
|
|
170
|
+
if (tailStrongClose.markup && tailStrongClose.markup !== '**') return false
|
|
171
|
+
|
|
172
|
+
tailText.content = (tailText.content || '') + '**'
|
|
173
|
+
const spacer = cloneTextLike(middleText, '')
|
|
174
|
+
const replacement = [
|
|
175
|
+
headText,
|
|
176
|
+
innerStrongOpen,
|
|
177
|
+
middleText,
|
|
178
|
+
tailStrongClose,
|
|
179
|
+
spacer,
|
|
180
|
+
...tokens.slice(linkOpenIdx, closeIdx + 1),
|
|
181
|
+
tailText
|
|
182
|
+
]
|
|
183
|
+
tokens.splice(startIdx, endIdx - startIdx + 1, ...replacement)
|
|
184
|
+
return true
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const tryFixBrokenRefLeadingCloseThenInnerStrongBeforeLinkTokenOnly = (tokens, startIdx, endIdx, linkCloseMap) => {
|
|
188
|
+
if (!tokens || startIdx < 0 || endIdx <= startIdx + 6) return false
|
|
189
|
+
|
|
190
|
+
const leadText = tokens[startIdx]
|
|
191
|
+
const leadingStrongClose = tokens[startIdx + 1]
|
|
192
|
+
const headText = tokens[startIdx + 2]
|
|
193
|
+
const innerStrongOpen = tokens[startIdx + 3]
|
|
194
|
+
const innerText = tokens[startIdx + 4]
|
|
195
|
+
const innerStrongClose = tokens[startIdx + 5]
|
|
196
|
+
|
|
197
|
+
if (!leadText || leadText.type !== 'text' || !leadText.content) return false
|
|
198
|
+
if (leadText.content.indexOf('[') === -1) return false
|
|
199
|
+
if (!leadingStrongClose || leadingStrongClose.type !== 'strong_close') return false
|
|
200
|
+
if (!headText || headText.type !== 'text' || !headText.content) return false
|
|
201
|
+
if (headText.content.indexOf('[') === -1) return false
|
|
202
|
+
if (!innerStrongOpen || innerStrongOpen.type !== 'strong_open') return false
|
|
203
|
+
if (!innerText || innerText.type !== 'text') return false
|
|
204
|
+
if (!innerStrongClose || innerStrongClose.type !== 'strong_close') return false
|
|
205
|
+
|
|
206
|
+
if ((leadingStrongClose.markup && leadingStrongClose.markup !== '**') ||
|
|
207
|
+
(innerStrongOpen.markup && innerStrongOpen.markup !== '**') ||
|
|
208
|
+
(innerStrongClose.markup && innerStrongClose.markup !== '**')) {
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let linkOpenIdx = -1
|
|
213
|
+
for (let i = startIdx + 6; i <= endIdx; i++) {
|
|
214
|
+
const token = tokens[i]
|
|
215
|
+
if (!token || token.type === 'text') continue
|
|
216
|
+
if (token.type !== 'link_open') return false
|
|
217
|
+
linkOpenIdx = i
|
|
218
|
+
break
|
|
219
|
+
}
|
|
220
|
+
if (linkOpenIdx === -1) return false
|
|
221
|
+
|
|
222
|
+
const closeIdx = linkCloseMap ? (linkCloseMap.get(linkOpenIdx) ?? -1) : -1
|
|
223
|
+
if (closeIdx === -1 || closeIdx !== endIdx) return false
|
|
224
|
+
|
|
225
|
+
for (let i = startIdx + 6; i <= endIdx; i++) {
|
|
226
|
+
const token = tokens[i]
|
|
227
|
+
if (!token || !token.type) continue
|
|
228
|
+
if (token.type === 'em_open' ||
|
|
229
|
+
token.type === 'em_close' ||
|
|
230
|
+
token.type === 'strong_open' ||
|
|
231
|
+
token.type === 'strong_close') {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
toStrongToken(leadingStrongClose, true)
|
|
237
|
+
toStrongToken(innerStrongOpen, false)
|
|
238
|
+
innerText.content = (innerText.content || '') + (innerStrongClose.markup || '**')
|
|
239
|
+
|
|
240
|
+
const replacement = [
|
|
241
|
+
leadText,
|
|
242
|
+
leadingStrongClose,
|
|
243
|
+
headText,
|
|
244
|
+
innerStrongOpen,
|
|
245
|
+
innerText,
|
|
246
|
+
...tokens.slice(linkOpenIdx, closeIdx + 1)
|
|
247
|
+
]
|
|
248
|
+
tokens.splice(startIdx, endIdx - startIdx + 1, ...replacement)
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const hasStrongAroundLinkFastPathSignature = (tokens, startIdx, endIdx, linkCloseMap) => {
|
|
253
|
+
if (!tokens || startIdx < 1 || endIdx <= startIdx + 6) return false
|
|
254
|
+
const outerOpen = tokens[startIdx - 1]
|
|
255
|
+
const headText = tokens[startIdx]
|
|
256
|
+
const earlyStrongClose = tokens[startIdx + 1]
|
|
257
|
+
const middleText = tokens[startIdx + 2]
|
|
258
|
+
const innerStrongOpen = tokens[startIdx + 3]
|
|
259
|
+
const linkOpen = tokens[startIdx + 4]
|
|
260
|
+
if (!outerOpen || outerOpen.type !== 'strong_open') return false
|
|
261
|
+
if (!headText || headText.type !== 'text' || !headText.content) return false
|
|
262
|
+
if (!earlyStrongClose || earlyStrongClose.type !== 'strong_close') return false
|
|
263
|
+
if (!middleText || middleText.type !== 'text' || !middleText.content) return false
|
|
264
|
+
if (!innerStrongOpen || innerStrongOpen.type !== 'strong_open') return false
|
|
265
|
+
if (!linkOpen || linkOpen.type !== 'link_open') return false
|
|
266
|
+
const linkOpenIdx = startIdx + 4
|
|
267
|
+
const closeIdx = linkCloseMap ? (linkCloseMap.get(linkOpenIdx) ?? -1) : -1
|
|
268
|
+
if (closeIdx === -1) return false
|
|
269
|
+
const tailTextIdx = closeIdx + 1
|
|
270
|
+
const tailStrongCloseIdx = closeIdx + 2
|
|
271
|
+
if (tailStrongCloseIdx !== endIdx) return false
|
|
272
|
+
const tailText = tokens[tailTextIdx]
|
|
273
|
+
const tailStrongClose = tokens[tailStrongCloseIdx]
|
|
274
|
+
if (!tailText || tailText.type !== 'text') return false
|
|
275
|
+
if (!tailStrongClose || tailStrongClose.type !== 'strong_close') return false
|
|
276
|
+
return true
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const hasLeadingCloseThenInnerStrongFastPathSignature = (tokens, startIdx, endIdx, linkCloseMap) => {
|
|
280
|
+
if (!tokens || startIdx < 0 || endIdx <= startIdx + 6) return false
|
|
281
|
+
const leadText = tokens[startIdx]
|
|
282
|
+
const leadingStrongClose = tokens[startIdx + 1]
|
|
283
|
+
const headText = tokens[startIdx + 2]
|
|
284
|
+
const innerStrongOpen = tokens[startIdx + 3]
|
|
285
|
+
const innerText = tokens[startIdx + 4]
|
|
286
|
+
const innerStrongClose = tokens[startIdx + 5]
|
|
287
|
+
if (!leadText || leadText.type !== 'text' || !leadText.content || leadText.content.indexOf('[') === -1) return false
|
|
288
|
+
if (!leadingStrongClose || leadingStrongClose.type !== 'strong_close') return false
|
|
289
|
+
if (!headText || headText.type !== 'text' || !headText.content || headText.content.indexOf('[') === -1) return false
|
|
290
|
+
if (!innerStrongOpen || innerStrongOpen.type !== 'strong_open') return false
|
|
291
|
+
if (!innerText || innerText.type !== 'text') return false
|
|
292
|
+
if (!innerStrongClose || innerStrongClose.type !== 'strong_close') return false
|
|
293
|
+
let linkOpenIdx = -1
|
|
294
|
+
for (let i = startIdx + 6; i <= endIdx; i++) {
|
|
295
|
+
const token = tokens[i]
|
|
296
|
+
if (!token || token.type === 'text') continue
|
|
297
|
+
if (token.type !== 'link_open') return false
|
|
298
|
+
linkOpenIdx = i
|
|
299
|
+
break
|
|
300
|
+
}
|
|
301
|
+
if (linkOpenIdx === -1) return false
|
|
302
|
+
const closeIdx = linkCloseMap ? (linkCloseMap.get(linkOpenIdx) ?? -1) : -1
|
|
303
|
+
if (closeIdx === -1 || closeIdx !== endIdx) return false
|
|
304
|
+
return true
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE = 0
|
|
308
|
+
const BROKEN_REF_FAST_PATH_RESULT_NO_MATCH = -1
|
|
309
|
+
|
|
310
|
+
const BROKEN_REF_TOKEN_ONLY_FAST_PATHS = [
|
|
311
|
+
{
|
|
312
|
+
name: 'strong-around-link',
|
|
313
|
+
hasSignature: hasStrongAroundLinkFastPathSignature,
|
|
314
|
+
apply: tryFixBrokenRefStrongAroundLinkTokenOnly
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'leading-close-then-inner-strong-before-link',
|
|
318
|
+
hasSignature: hasLeadingCloseThenInnerStrongFastPathSignature,
|
|
319
|
+
apply: tryFixBrokenRefLeadingCloseThenInnerStrongBeforeLinkTokenOnly
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
const applyBrokenRefTokenOnlyFastPath = (tokens, startIdx, endIdx, linkCloseMap, metrics = null, bumpMetric = null) => {
|
|
324
|
+
let hasActiveSignature = false
|
|
325
|
+
for (let i = 0; i < BROKEN_REF_TOKEN_ONLY_FAST_PATHS.length; i++) {
|
|
326
|
+
const fastPath = BROKEN_REF_TOKEN_ONLY_FAST_PATHS[i]
|
|
327
|
+
if (!fastPath) continue
|
|
328
|
+
if (typeof fastPath.hasSignature === 'function') {
|
|
329
|
+
if (!fastPath.hasSignature(tokens, startIdx, endIdx, linkCloseMap)) continue
|
|
330
|
+
}
|
|
331
|
+
hasActiveSignature = true
|
|
332
|
+
if (typeof fastPath.apply !== 'function') continue
|
|
333
|
+
if (!fastPath.apply(tokens, startIdx, endIdx, linkCloseMap)) continue
|
|
334
|
+
if (typeof bumpMetric === 'function') {
|
|
335
|
+
bumpMetric(metrics, 'brokenRefFastPaths', fastPath.name)
|
|
336
|
+
}
|
|
337
|
+
return 1
|
|
338
|
+
}
|
|
339
|
+
if (!hasActiveSignature) return BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE
|
|
340
|
+
return BROKEN_REF_FAST_PATH_RESULT_NO_MATCH
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export {
|
|
344
|
+
BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE,
|
|
345
|
+
BROKEN_REF_FAST_PATH_RESULT_NO_MATCH,
|
|
346
|
+
applyBrokenRefTokenOnlyFastPath,
|
|
347
|
+
tryFixTailPatternTokenOnly,
|
|
348
|
+
tryFixTailDanglingStrongCloseTokenOnly
|
|
349
|
+
}
|