@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
package/index.js
CHANGED
|
@@ -1,62 +1,49 @@
|
|
|
1
|
-
import { hasCjkBreaksRule,
|
|
1
|
+
import { hasCjkBreaksRule, ensureCoreRuleOrder, deriveOptionInfo } from './src/token-utils.js'
|
|
2
2
|
import { patchScanDelims } from './src/token-core.js'
|
|
3
3
|
import { registerTokenCompat } from './src/token-compat.js'
|
|
4
4
|
import { registerTokenPostprocess } from './src/token-postprocess.js'
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const DEFAULT_OPTION = {
|
|
7
|
+
mditAttrs: true, // assume markdown-it-attrs integration by default
|
|
8
|
+
mode: 'japanese', // 'japanese'(->japanese-boundary-guard) | 'japanese-boundary' | 'japanese-boundary-guard' | 'aggressive' | 'compatible'
|
|
9
|
+
coreRulesBeforePostprocess: [], // e.g. ['cjk_breaks'] to keep rules ahead of postprocess
|
|
10
|
+
postprocess: true, // enable link/ref reconstruction pass
|
|
11
|
+
patchCorePush: true // keep restore-softbreaks after late cjk_breaks
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const buildNormalizedOption = (md, option) => {
|
|
15
|
+
const opt = { ...DEFAULT_OPTION }
|
|
16
|
+
if (option) Object.assign(opt, option)
|
|
17
|
+
opt.hasCjkBreaks = hasCjkBreaksRule(md)
|
|
18
|
+
deriveOptionInfo(opt)
|
|
19
|
+
return opt
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const writeSharedOption = (target, source) => {
|
|
23
|
+
for (const key of Object.keys(target)) {
|
|
24
|
+
delete target[key]
|
|
18
25
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const noLink = new md.constructor(md.options)
|
|
22
|
-
mditStrongJa(noLink, { ...baseOpt, _skipPostprocess: true })
|
|
23
|
-
noLink.inline.ruler.disable(['link'])
|
|
24
|
-
cache.set(key, noLink)
|
|
25
|
-
return noLink
|
|
26
|
+
Object.assign(target, source)
|
|
27
|
+
return target
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
const mditStrongJa = (md, option) => {
|
|
29
31
|
if (option && typeof option.engine === 'string' && option.engine !== 'token') {
|
|
30
32
|
throw new Error('mditStrongJa: legacy engine was removed; use token (default)')
|
|
31
33
|
}
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
coreRulesBeforePostprocess: [], // e.g. ['cjk_breaks'] to keep rules ahead of postprocess
|
|
37
|
-
postprocess: true, // enable link/ref reconstruction pass
|
|
38
|
-
patchCorePush: true // keep restore-softbreaks after late cjk_breaks
|
|
39
|
-
}
|
|
40
|
-
if (option) Object.assign(opt, option)
|
|
41
|
-
opt.hasCjkBreaks = hasCjkBreaksRule(md)
|
|
34
|
+
const nextOpt = buildNormalizedOption(md, option)
|
|
35
|
+
const opt = md.__strongJaTokenOpt && typeof md.__strongJaTokenOpt === 'object'
|
|
36
|
+
? writeSharedOption(md.__strongJaTokenOpt, nextOpt)
|
|
37
|
+
: nextOpt
|
|
42
38
|
|
|
43
39
|
md.__strongJaTokenOpt = opt
|
|
44
40
|
patchScanDelims(md)
|
|
45
41
|
registerTokenCompat(md, opt)
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const rawCoreRules = opt.coreRulesBeforePostprocess
|
|
50
|
-
const hasCoreRuleConfig = Array.isArray(rawCoreRules)
|
|
51
|
-
? rawCoreRules.length > 0
|
|
52
|
-
: !!rawCoreRules
|
|
53
|
-
const coreRulesBeforePostprocess = hasCoreRuleConfig
|
|
54
|
-
? normalizeCoreRulesBeforePostprocess(rawCoreRules)
|
|
55
|
-
: []
|
|
56
|
-
ensureCoreRuleOrder(md, coreRulesBeforePostprocess, 'strong_ja_token_postprocess')
|
|
57
|
-
}
|
|
43
|
+
registerTokenPostprocess(md, opt)
|
|
44
|
+
ensureCoreRuleOrder(md, opt.__strongJaNormalizedCoreRulesBeforePostprocess, 'strong_ja_token_postprocess')
|
|
58
45
|
|
|
59
46
|
return md
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
export default mditStrongJa
|
|
49
|
+
export default mditStrongJa
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-strong-ja",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "Extends asterisk emphasis handling for Japanese text while keeping markdown-it behavior as close as practical.",
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -11,7 +11,23 @@
|
|
|
11
11
|
"LICENSE"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node test/test.js"
|
|
14
|
+
"test": "node test/test.js",
|
|
15
|
+
"test:fixtures": "node test/test-fixtures.js",
|
|
16
|
+
"test:edge": "node test/test-edge.js",
|
|
17
|
+
"test:postprocess": "node test/post-processing.test.js",
|
|
18
|
+
"test:postprocess-fastpath": "node test/post-processing-fastpath.test.js",
|
|
19
|
+
"test:postprocess-fastpath-roster": "node test/post-processing-fastpath-roster.test.js",
|
|
20
|
+
"test:postprocess-flow": "node test/post-processing-flow.test.js",
|
|
21
|
+
"test:postprocess-link-helper": "node test/post-processing-link-helper.test.js",
|
|
22
|
+
"test:postprocess-gate": "node test/postprocess-gate.js",
|
|
23
|
+
"test:tokenonly-progress": "node test/post-processing-progress.test.js",
|
|
24
|
+
"test:readme": "node test/test-readme.js",
|
|
25
|
+
"test:map": "node test/test-map.js",
|
|
26
|
+
"test:all": "node test/test-all.js",
|
|
27
|
+
"bench:scan": "node test/material/perf-scan-delims.mjs",
|
|
28
|
+
"bench:postprocess": "node test/material/perf-postprocess.mjs",
|
|
29
|
+
"analyze:postprocess-calls": "node test/material/analyze-postprocess-calls.mjs",
|
|
30
|
+
"analyze:fastpath": "node test/material/analyze-fastpath-hits.mjs"
|
|
15
31
|
},
|
|
16
32
|
"repository": "https://github.com/peaceroad/p7d-markdown-it-strong-ja.git",
|
|
17
33
|
"author": "peaceroad <peaceroad@gmail.com>",
|
|
@@ -20,10 +36,14 @@
|
|
|
20
36
|
"markdown-it": "^14.1.0"
|
|
21
37
|
},
|
|
22
38
|
"devDependencies": {
|
|
23
|
-
"@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.
|
|
24
|
-
"@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.
|
|
39
|
+
"@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.8",
|
|
40
|
+
"@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.10.0",
|
|
41
|
+
"@peaceroad/markdown-it-renderer-image": "^0.12.0",
|
|
42
|
+
"@peaceroad/markdown-it-renderer-inline-text": "^0.7.0",
|
|
25
43
|
"markdown-it-attrs": "^4.3.1",
|
|
26
44
|
"markdown-it-sub": "^2.0.0",
|
|
27
|
-
"markdown-it-sup": "^2.0.0"
|
|
45
|
+
"markdown-it-sup": "^2.0.0",
|
|
46
|
+
"p7d-markdown-it-p-captions": "^0.21.0"
|
|
28
47
|
}
|
|
29
48
|
}
|
|
49
|
+
|
package/src/token-compat.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
REG_ATTRS,
|
|
4
4
|
isJapaneseChar,
|
|
5
5
|
hasCjkBreaksRule,
|
|
6
|
+
isCjkBreaksRuleName,
|
|
6
7
|
getRuntimeOpt,
|
|
7
8
|
moveRuleAfter
|
|
8
9
|
} from './token-utils.js'
|
|
@@ -13,14 +14,43 @@ const isAsciiWordCode = (code) => {
|
|
|
13
14
|
(code >= 0x61 && code <= 0x7A)
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
const trimTrailingSpaceTab = (text) => {
|
|
18
|
+
if (!text) return text
|
|
19
|
+
let end = text.length
|
|
20
|
+
while (end > 0) {
|
|
21
|
+
const code = text.charCodeAt(end - 1)
|
|
22
|
+
if (code !== 0x20 && code !== 0x09) break
|
|
23
|
+
end--
|
|
24
|
+
}
|
|
25
|
+
return end === text.length ? text : text.slice(0, end)
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
const registerTokenCompat = (md, baseOpt) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
const isCompatibleMode = (state) => {
|
|
30
|
+
const override = state && state.env && state.env.__strongJaTokenOpt
|
|
31
|
+
if (!override) return baseOpt.__strongJaIsCompatibleMode === true
|
|
32
|
+
const opt = getRuntimeOpt(state, baseOpt)
|
|
33
|
+
return opt.__strongJaIsCompatibleMode === true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let hasTextJoinRule = false
|
|
37
|
+
const coreRules = md.core && md.core.ruler && Array.isArray(md.core.ruler.__rules__)
|
|
38
|
+
? md.core.ruler.__rules__
|
|
39
|
+
: null
|
|
40
|
+
if (coreRules) {
|
|
41
|
+
for (let i = 0; i < coreRules.length; i++) {
|
|
42
|
+
const rule = coreRules[i]
|
|
43
|
+
if (rule && rule.name === 'text_join') {
|
|
44
|
+
hasTextJoinRule = true
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
20
49
|
|
|
21
50
|
if (!md.__strongJaTokenTrimTrailingRegistered) {
|
|
22
51
|
md.__strongJaTokenTrimTrailingRegistered = true
|
|
23
52
|
const trimInlineTrailingSpaces = (state) => {
|
|
53
|
+
if (isCompatibleMode(state)) return
|
|
24
54
|
if (!state || !state.tokens) return
|
|
25
55
|
for (let i = 0; i < state.tokens.length; i++) {
|
|
26
56
|
const token = state.tokens[i]
|
|
@@ -32,7 +62,9 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
32
62
|
if (idx < 0) continue
|
|
33
63
|
const tail = token.children[idx]
|
|
34
64
|
if (!tail || tail.type !== 'text' || !tail.content) continue
|
|
35
|
-
const
|
|
65
|
+
const lastCode = tail.content.charCodeAt(tail.content.length - 1)
|
|
66
|
+
if (lastCode !== 0x20 && lastCode !== 0x09) continue
|
|
67
|
+
const trimmed = trimTrailingSpaceTab(tail.content)
|
|
36
68
|
if (trimmed !== tail.content) {
|
|
37
69
|
tail.content = trimmed
|
|
38
70
|
}
|
|
@@ -48,6 +80,7 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
48
80
|
if (!md.__strongJaTokenSoftbreakSpacingRegistered) {
|
|
49
81
|
md.__strongJaTokenSoftbreakSpacingRegistered = true
|
|
50
82
|
const normalizeSoftbreakSpacing = (state) => {
|
|
83
|
+
if (isCompatibleMode(state)) return
|
|
51
84
|
if (!state) return
|
|
52
85
|
if (baseOpt.hasCjkBreaks !== true && state.md) {
|
|
53
86
|
baseOpt.hasCjkBreaks = hasCjkBreaksRule(state.md)
|
|
@@ -57,29 +90,37 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
57
90
|
for (let i = 0; i < state.tokens.length; i++) {
|
|
58
91
|
const token = state.tokens[i]
|
|
59
92
|
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|
|
93
|
+
const children = token.children
|
|
60
94
|
let hasEmphasis = false
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
let hasBreakCandidate = false
|
|
96
|
+
for (let j = 0; j < children.length; j++) {
|
|
97
|
+
const child = children[j]
|
|
63
98
|
if (!child) continue
|
|
64
99
|
if (child.type === 'strong_open' || child.type === 'strong_close' || child.type === 'em_open' || child.type === 'em_close') {
|
|
65
100
|
hasEmphasis = true
|
|
66
|
-
break
|
|
67
101
|
}
|
|
102
|
+
if (!hasBreakCandidate &&
|
|
103
|
+
(child.type === 'softbreak' ||
|
|
104
|
+
(child.type === 'text' && child.content && child.content.indexOf('\n') !== -1))) {
|
|
105
|
+
hasBreakCandidate = true
|
|
106
|
+
}
|
|
107
|
+
if (hasEmphasis && hasBreakCandidate) break
|
|
68
108
|
}
|
|
69
109
|
if (!hasEmphasis) continue
|
|
70
|
-
|
|
71
|
-
|
|
110
|
+
if (!hasBreakCandidate) continue
|
|
111
|
+
for (let j = 0; j < children.length; j++) {
|
|
112
|
+
const child = children[j]
|
|
72
113
|
if (!child) continue
|
|
73
114
|
if (child.type === 'softbreak') {
|
|
74
|
-
const prevToken =
|
|
75
|
-
const nextToken =
|
|
115
|
+
const prevToken = children[j - 1]
|
|
116
|
+
const nextToken = children[j + 1]
|
|
76
117
|
if (!prevToken || !nextToken) continue
|
|
77
118
|
if (prevToken.type !== 'text' || !prevToken.content) continue
|
|
78
119
|
if (nextToken.type !== 'text' || !nextToken.content) continue
|
|
79
120
|
const prevCharCode = prevToken.content.charCodeAt(prevToken.content.length - 1)
|
|
80
121
|
const nextCharCode = nextToken.content.charCodeAt(0)
|
|
81
122
|
const isAsciiWord = isAsciiWordCode(nextCharCode)
|
|
82
|
-
const shouldReplace = isAsciiWord &&
|
|
123
|
+
const shouldReplace = isAsciiWord &&
|
|
83
124
|
isJapaneseChar(prevCharCode) && !isJapaneseChar(nextCharCode)
|
|
84
125
|
if (!shouldReplace) continue
|
|
85
126
|
child.type = 'text'
|
|
@@ -90,7 +131,6 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
90
131
|
continue
|
|
91
132
|
}
|
|
92
133
|
if (child.type !== 'text' || !child.content) continue
|
|
93
|
-
if (!hasEmphasis) continue
|
|
94
134
|
if (child.content.indexOf('\n') === -1) continue
|
|
95
135
|
let normalized = ''
|
|
96
136
|
for (let idx = 0; idx < child.content.length; idx++) {
|
|
@@ -99,7 +139,7 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
99
139
|
const prevCharCode = idx > 0 ? child.content.charCodeAt(idx - 1) : 0
|
|
100
140
|
const nextCharCode = idx + 1 < child.content.length ? child.content.charCodeAt(idx + 1) : 0
|
|
101
141
|
const isAsciiWord = isAsciiWordCode(nextCharCode)
|
|
102
|
-
const shouldReplace = isAsciiWord &&
|
|
142
|
+
const shouldReplace = isAsciiWord &&
|
|
103
143
|
isJapaneseChar(prevCharCode) && !isJapaneseChar(nextCharCode)
|
|
104
144
|
if (shouldReplace) {
|
|
105
145
|
normalized += ' '
|
|
@@ -122,9 +162,13 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
const restoreSoftbreaksAfterCjk = (state) => {
|
|
165
|
+
if (isCompatibleMode(state)) return
|
|
125
166
|
if (!state) return
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
167
|
+
const overrideOpt = state.env && state.env.__strongJaTokenOpt
|
|
168
|
+
if (overrideOpt) {
|
|
169
|
+
const opt = getRuntimeOpt(state, baseOpt)
|
|
170
|
+
if (opt.mditAttrs !== false) return
|
|
171
|
+
}
|
|
128
172
|
if (!state.md || state.md.__strongJaRestoreSoftbreaksForAttrs !== true) return
|
|
129
173
|
if (baseOpt.hasCjkBreaks !== true && state.md) {
|
|
130
174
|
baseOpt.hasCjkBreaks = hasCjkBreaksRule(state.md)
|
|
@@ -167,16 +211,17 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
167
211
|
if (added !== false) {
|
|
168
212
|
md.__strongJaTokenRestoreRegistered = true
|
|
169
213
|
md.__strongJaRestoreSoftbreaksForAttrs = baseOpt.mditAttrs === false
|
|
170
|
-
if (baseOpt.hasCjkBreaks) {
|
|
171
|
-
moveRuleAfter(md.core.ruler, 'strong_ja_restore_softbreaks', 'cjk_breaks')
|
|
172
|
-
}
|
|
173
214
|
if (baseOpt.patchCorePush !== false && !md.__strongJaTokenPatchCorePush) {
|
|
174
215
|
md.__strongJaTokenPatchCorePush = true
|
|
175
216
|
const originalPush = md.core.ruler.push.bind(md.core.ruler)
|
|
176
217
|
md.core.ruler.push = (name, fn, options) => {
|
|
177
218
|
const res = originalPush(name, fn, options)
|
|
178
|
-
if (name
|
|
219
|
+
if (isCjkBreaksRuleName(name)) {
|
|
179
220
|
baseOpt.hasCjkBreaks = true
|
|
221
|
+
md.__strongJaHasCjkBreaks = true
|
|
222
|
+
if (Array.isArray(md.core.ruler.__rules__)) {
|
|
223
|
+
md.__strongJaCjkBreaksRuleCount = md.core.ruler.__rules__.length
|
|
224
|
+
}
|
|
180
225
|
moveRuleAfter(md.core.ruler, 'strong_ja_restore_softbreaks', name)
|
|
181
226
|
}
|
|
182
227
|
return res
|
|
@@ -192,9 +237,13 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
192
237
|
if (baseOpt.mditAttrs !== false && !md.__strongJaTokenPreAttrsRegistered) {
|
|
193
238
|
md.__strongJaTokenPreAttrsRegistered = true
|
|
194
239
|
md.core.ruler.before('linkify', 'strong_ja_token_pre_attrs', (state) => {
|
|
240
|
+
if (isCompatibleMode(state)) return
|
|
195
241
|
if (!state || !state.tokens) return
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
242
|
+
const overrideOpt = state.env && state.env.__strongJaTokenOpt
|
|
243
|
+
if (overrideOpt) {
|
|
244
|
+
const opt = getRuntimeOpt(state, baseOpt)
|
|
245
|
+
if (opt.mditAttrs === false) return
|
|
246
|
+
}
|
|
198
247
|
for (let i = 0; i < state.tokens.length; i++) {
|
|
199
248
|
const token = state.tokens[i]
|
|
200
249
|
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|