@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/index.js CHANGED
@@ -1,62 +1,49 @@
1
- import { hasCjkBreaksRule, normalizeCoreRulesBeforePostprocess, ensureCoreRuleOrder, resolveMode } from './src/token-utils.js'
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 buildNoLinkCacheKey = (opt) => {
7
- const mode = resolveMode(opt)
8
- const mditAttrs = opt && opt.mditAttrs === false ? '0' : '1'
9
- const mdBreaks = opt && opt.mdBreaks === true ? '1' : '0'
10
- return `${mode}|${mditAttrs}|${mdBreaks}`
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 getNoLinkMdInstance = (md, opt) => {
14
- const baseOpt = opt || md.__strongJaTokenOpt || { mode: 'japanese' }
15
- const key = buildNoLinkCacheKey(baseOpt)
16
- if (!md.__strongJaTokenNoLinkCache) {
17
- md.__strongJaTokenNoLinkCache = new Map()
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
- const cache = md.__strongJaTokenNoLinkCache
20
- if (cache.has(key)) return cache.get(key)
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 opt = {
33
- mditAttrs: true, // assume markdown-it-attrs integration by default
34
- mdBreaks: md.options.breaks, // inherit md.options.breaks for compat handling
35
- mode: 'japanese', // 'japanese' | 'aggressive' | 'compatible' (pairing behavior)
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
- if (!opt._skipPostprocess) {
48
- registerTokenPostprocess(md, opt, getNoLinkMdInstance)
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": "This is a plugin for markdown-it. It is an alternative to the standard `**` (strong) and `*` (em) processing. It also processes strings that cannot be converted by the standard.",
4
- "version": "0.7.2",
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.5",
24
- "@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.8.2",
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
+
@@ -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 hasTextJoinRule = Array.isArray(md.core?.ruler?.__rules__)
18
- ? md.core.ruler.__rules__.some((rule) => rule && rule.name === 'text_join')
19
- : false
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 trimmed = tail.content.replace(/[ \t]+$/, '')
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
- for (let j = 0; j < token.children.length; j++) {
62
- const child = token.children[j]
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
- for (let j = 0; j < token.children.length; j++) {
71
- const child = token.children[j]
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 = token.children[j - 1]
75
- const nextToken = token.children[j + 1]
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 && nextCharCode !== 0x7B && nextCharCode !== 0x5C &&
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 && nextCharCode !== 0x7B && nextCharCode !== 0x5C &&
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 opt = getRuntimeOpt(state, baseOpt)
127
- if (opt.mditAttrs !== false) return
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 && name.indexOf && name.indexOf('cjk_breaks') !== -1) {
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 opt = getRuntimeOpt(state, baseOpt)
197
- if (opt.mditAttrs === false) return
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