mirror of
https://github.com/abort-retry-ignore/joplock.git
synced 2026-10-19 10:13:51 +00:00
fix(rendered): widen fencedCodeLanguage filter to all <pre>
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
A bare <pre> (no <code> child, no language-* class) from pasted external HTML fell through to Turndown's default fenced-code rule, which does not widen the fence when the content contains a backtick run and instead inline-escapes the backticks — producing broken markdown. Match every <pre>; the replacement already reads textContent when there is no <code> child, so bare <pre> is fenced (and fence-widened) safely. Add regression tests for the bare-<pre> backtick-run case and the normal language-tagged <pre><code> path.
This commit is contained in:
parent
4978590b01
commit
5d251cc015
2 changed files with 35 additions and 1 deletions
|
|
@ -3802,7 +3802,12 @@ function getTurndown(){
|
|||
if(_tdService)return _tdService;
|
||||
var td=new TurndownService({headingStyle:'atx',hr:'---',codeBlockStyle:'fenced',bulletListMarker:'-',emDelimiter:'*',strongDelimiter:'**',br:''});
|
||||
// Preserve fenced code language from TinyMCE codesample blocks.
|
||||
td.addRule('fencedCodeLanguage',{filter:function(n){return n.nodeName==='PRE'&&(!!n.querySelector('code')||/(?:^|\s)language-/.test(n.getAttribute('class')||''))},replacement:function(_c,n){
|
||||
// Match ALL <pre> (not only <pre><code> or language-* blocks): a bare <pre>
|
||||
// from pasted external HTML would otherwise fall through to Turndown's default
|
||||
// fenced-code rule, which does NOT widen the fence when the content contains a
|
||||
// backtick run — producing broken markdown. The replacement below reads
|
||||
// textContent when there is no <code> child, so bare <pre> is handled safely.
|
||||
td.addRule('fencedCodeLanguage',{filter:function(n){return n.nodeName==='PRE'},replacement:function(_c,n){
|
||||
var codeEl=n.querySelector('code');
|
||||
var cls=((codeEl&&codeEl.getAttribute('class'))||n.getAttribute('class')||'');
|
||||
var m=cls.match(/(?:^|\s)language-([\w-]+)/);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,35 @@ test('tinymceToMarkdown preserves a blank line INSIDE a code block (C #include i
|
|||
`blank line inside code block must be preserved, got: ${JSON.stringify(result)}`);
|
||||
});
|
||||
|
||||
test('tinymceToMarkdown widens fence for a bare <pre> containing a backtick run', () => {
|
||||
// A bare <pre> (no <code> child, no language-* class) can arrive from pasted
|
||||
// external HTML. It must still be emitted as a fenced code block whose fence
|
||||
// is WIDER than any backtick run inside it — otherwise the markdown re-parses
|
||||
// wrong. Before widening the fencedCodeLanguage filter, a bare <pre> fell
|
||||
// through to Turndown's default rule, which escapes backticks inline instead.
|
||||
const ctx = makeTurndownCtx();
|
||||
runWithDeps(ctx, 'getTurndown', 'tinymceToMarkdown');
|
||||
const html = '<pre>const x = ```` here</pre>';
|
||||
const result = vm.runInContext('tinymceToMarkdown(' + JSON.stringify(html) + ')', ctx);
|
||||
// Fence must be at least 5 backticks (one longer than the 4-backtick run),
|
||||
// and the backtick run must survive verbatim (not be backslash-escaped).
|
||||
assert.ok(/`{5,}\n?const x = ```` here\n?`{5,}/.test(result),
|
||||
`bare <pre> must produce a widened fence, got: ${JSON.stringify(result)}`);
|
||||
assert.ok(!/\\`/.test(result),
|
||||
`backticks must not be inline-escaped, got: ${JSON.stringify(result)}`);
|
||||
});
|
||||
|
||||
test('tinymceToMarkdown still fences a normal <pre><code> block with language', () => {
|
||||
// Regression guard: widening the filter to all <pre> must not break the
|
||||
// common <pre class="language-x"><code>…</code></pre> path.
|
||||
const ctx = makeTurndownCtx();
|
||||
runWithDeps(ctx, 'getTurndown', 'tinymceToMarkdown');
|
||||
const html = '<pre class="language-js"><code>const y = 1;</code></pre>';
|
||||
const result = vm.runInContext('tinymceToMarkdown(' + JSON.stringify(html) + ')', ctx);
|
||||
assert.ok(/```js\nconst y = 1;\n```/.test(result),
|
||||
`language-tagged fenced block must round-trip, got: ${JSON.stringify(result)}`);
|
||||
});
|
||||
|
||||
test('tinymceToMarkdown still collapses a blank line after a real ATX heading', () => {
|
||||
const ctx = makeTurndownCtx();
|
||||
runWithDeps(ctx, 'getTurndown', 'tinymceToMarkdown');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue