@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.
@@ -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
+ }