Port the two master-side markdown fixes onto the mdast renderer: cjkFriendlyStrong joins both parse.ts grammars as a micromark extension (ex remark plugin), and inline-code HTTP(S) URLs gain the safe anchor in render.tsx without normalizeUri. Re-record the two new web-e2e goldens whose footer clock spacing diverged from the nine established goldens, and drop the timing-flaky LLM duration segment one of them captured.
84 lines
2.9 KiB
TypeScript
84 lines
2.9 KiB
TypeScript
/** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
|
|
|
|
import { attention } from 'micromark-core-commonmark'
|
|
import { unicodePunctuation } from 'micromark-util-character'
|
|
import { classifyCharacter } from 'micromark-util-classify-character'
|
|
import { codes, constants } from 'micromark-util-symbol'
|
|
import type { Construct, Extension, State, Tokenizer } from 'micromark-util-types'
|
|
|
|
const cjkCharacter = new RegExp([
|
|
'\\p{Script_Extensions=Han}',
|
|
'\\p{Script_Extensions=Hiragana}',
|
|
'\\p{Script_Extensions=Katakana}',
|
|
'\\p{Script_Extensions=Hangul}',
|
|
'\\p{Script_Extensions=Bopomofo}',
|
|
].join('|'), 'u')
|
|
|
|
function isCjkCharacter(code: number | null): boolean {
|
|
return code !== null && code >= 0 && cjkCharacter.test(String.fromCodePoint(code))
|
|
}
|
|
|
|
const tokenizeCjkFriendlyAttention: Tokenizer = function (effects, ok, nok) {
|
|
const configuredAttentionMarkers = this.parser.constructs.attentionMarkers.null
|
|
if (configuredAttentionMarkers === undefined) {
|
|
throw new Error('micromark CommonMark attention markers are unavailable')
|
|
}
|
|
const attentionMarkers = configuredAttentionMarkers
|
|
const previous = this.previous
|
|
const before = classifyCharacter(previous)
|
|
let marker: number | null = codes.eof
|
|
|
|
return start
|
|
|
|
function start(code: number | null): State | undefined {
|
|
/* v8 ignore next -- this text construct is dispatched only for an asterisk. */
|
|
if (code !== codes.asterisk) return nok(code)
|
|
marker = code
|
|
effects.enter('attentionSequence')
|
|
return inside(code)
|
|
}
|
|
|
|
function inside(code: number | null): State | undefined {
|
|
if (code === marker) {
|
|
effects.consume(code)
|
|
return inside
|
|
}
|
|
|
|
const token = effects.exit('attentionSequence')
|
|
const after = classifyCharacter(code)
|
|
const open = !after || (after === constants.characterGroupPunctuation && Boolean(before))
|
|
|| attentionMarkers.includes(code)
|
|
const commonMarkClose = !before
|
|
|| (before === constants.characterGroupPunctuation && Boolean(after))
|
|
|| attentionMarkers.includes(previous)
|
|
const markerCount = token.end.offset - token.start.offset
|
|
const cjkStrongClose = markerCount >= 2
|
|
&& unicodePunctuation(previous)
|
|
&& isCjkCharacter(code)
|
|
const close = commonMarkClose || cjkStrongClose
|
|
|
|
token._open = open
|
|
token._close = close
|
|
return ok(code)
|
|
}
|
|
}
|
|
|
|
const cjkFriendlyAttention: Construct = {
|
|
name: 'cjkFriendlyAttention',
|
|
resolveAll: attention.resolveAll,
|
|
tokenize: tokenizeCjkFriendlyAttention,
|
|
}
|
|
|
|
const cjkFriendlyStrongExtension: Extension = {
|
|
text: { [codes.asterisk]: cjkFriendlyAttention },
|
|
}
|
|
|
|
/**
|
|
* Extend CommonMark asterisk strong emphasis for punctuation-delimited CJK
|
|
* prose, as a micromark syntax extension for `fromMarkdown`.
|
|
* @returns The micromark syntax extension.
|
|
*/
|
|
export function cjkFriendlyStrong(): Extension {
|
|
return cjkFriendlyStrongExtension
|
|
}
|