Conversation
marked の emStrongRDelimAst regex は CommonMark 由来の制限により、
約物(`)` など)直後の `**` を closing delimiter として認識しない。
`marked.use()` で emStrong をオーバーライドし、group1 の lookahead に
`\p{L}` を追加することで、約物 + ** + 文字(CJK含む)の並びも
closing delimiter と見なすよう修正した。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request addresses an issue where closing bold delimiters are not correctly identified when following CJK punctuation by extending the marked tokenizer. Feedback points out a critical syntax error due to a missing Unicode flag and incorrect string escaping in the regex replacement. Additionally, it is recommended to use a scoped marked instance to avoid global side effects and to optimize the patching logic by moving it to the module level.
| this.rules.inline.emStrongRDelimAst = new RegExp( | ||
| orig.source.replace('(?=[\\s]|$)', '(?=[\\s\\p{L}]|$)'), | ||
| orig.flags | ||
| ); |
There was a problem hiding this comment.
This regex modification has two significant issues:
- Missing
uflag: The use of Unicode property escapes like\p{L}requires theu(unicode) flag to be set on the regular expression. Without this flag,new RegExp()will throw aSyntaxErrorin modern JavaScript environments (Node.js, modern browsers). - Incorrect string escaping: In the
replacecall, the string literal'[\s]'results in[s]because the backslash is not escaped within the string. To correctly match the literal\sin the regex source, you must use'[\\s]'(double backslash).
Additionally, consider if \p{P} (punctuation) should also be included in the lookahead to fully support CommonMark's flanking rules for CJK punctuation (e.g., **foo(bar)**。).
| this.rules.inline.emStrongRDelimAst = new RegExp( | |
| orig.source.replace('(?=[\\s]|$)', '(?=[\\s\\p{L}]|$)'), | |
| orig.flags | |
| ); | |
| this.rules.inline.emStrongRDelimAst = new RegExp( | |
| orig.source.replace('(?=[\\\\s]|$)', '(?=[\\\\s\\\\p{L}]|$)'), | |
| orig.flags.includes('u') ? orig.flags : orig.flags + 'u' | |
| ); |
| marked.use({ | ||
| tokenizer: { | ||
| emStrong(src, maskedSrc, prevChar = '') { | ||
| if (!this._cjkFixed) { | ||
| const orig = this.rules.inline.emStrongRDelimAst; | ||
| this.rules.inline.emStrongRDelimAst = new RegExp( | ||
| orig.source.replace('(?=[\\s]|$)', '(?=[\\s\\p{L}]|$)'), | ||
| orig.flags | ||
| ); | ||
| this._cjkFixed = true; | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| }); |
There was a problem hiding this comment.
Calling marked.use() at the top level of the module modifies the global marked instance. This side effect affects all other parts of the application that might use marked, which can lead to unexpected behavior or conflicts. It is recommended to use a scoped instance of marked (e.g., new marked.Marked()) and apply extensions to that instance instead of the global one.
| if (!this._cjkFixed) { | ||
| const orig = this.rules.inline.emStrongRDelimAst; | ||
| this.rules.inline.emStrongRDelimAst = new RegExp( | ||
| orig.source.replace('(?=[\\s]|$)', '(?=[\\s\\p{L}]|$)'), | ||
| orig.flags | ||
| ); | ||
| this._cjkFixed = true; | ||
| } |
There was a problem hiding this comment.
The _cjkFixed flag is being set on this (the tokenizer instance). Since marked typically creates a new tokenizer instance for each parse, this patching logic will re-run every time a new parse begins and encounters an emStrong token. While the replace operation is idempotent here, it is more efficient to use a module-level variable or apply the patch once outside the tokenizer to avoid redundant regex compilation and property assignment on every parse.
`this` はパース毎に新しいインスタンスになるため、インスタンスプロパティとして 設定した `_cjkFixed` は毎回 `undefined` になり意図通りに機能しない。 モジュールスコープの変数に移動することで、最初のパースでのみパッチを適用する という本来の意図を正しく実現する。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@gemini-code-assist レビューありがとうございます。各指摘について検討した結果をお伝えします。 High priority(
|
|
ご回答ありがとうございます。各点について承知いたしました。特に |
Summary
**太字(strong)**にしてのように CJK 閉じ括弧の直後に**があり、その後に通常テキストが続くと bold が正しくレンダリングされない問題を修正marked.use()でemStrongをオーバーライドし、emStrongRDelimAstの lookahead に\p{L}を追加することで CJK 文字の前でも closing delimiter と認識されるよう対処Test plan
npm testが全テスト通過することを確認**foo(bar)**baz**qux**→<strong>foo(bar)</strong>baz<strong>qux</strong>となることを確認🤖 Generated with Claude Code