From d080051e3240bec2c33157032407137105aec039 Mon Sep 17 00:00:00 2001 From: Sarath Francis Date: Sat, 18 Jul 2026 03:27:33 -0400 Subject: [PATCH] fix: fall back to default checkbox renderer when extension returns false A renderer extension named after a built-in token can return false to fall back to the default renderer. The Parser decides that from a list of built-in token type names, and 'checkbox' was never added to either the block or the inline list when the checkbox token was introduced. The result is that a `checkbox` renderer extension returning false emits nothing instead of falling back, so the checkbox silently disappears from the task item. --- src/Parser.ts | 4 ++-- test/unit/marked.test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Parser.ts b/src/Parser.ts index 636a8287b9..1fa984a2ad 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -50,7 +50,7 @@ export class _Parser { if (this.options.extensions?.renderers?.[anyToken.type]) { const genericToken = anyToken as Tokens.Generic; const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken); - if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'def', 'paragraph', 'text'].includes(genericToken.type)) { + if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'checkbox', 'html', 'def', 'paragraph', 'text'].includes(genericToken.type)) { out += ret || ''; continue; } @@ -136,7 +136,7 @@ export class _Parser { // Run any renderer extensions if (this.options.extensions?.renderers?.[anyToken.type]) { const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken); - if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) { + if (ret !== false || !['escape', 'html', 'link', 'image', 'checkbox', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) { out += ret || ''; continue; } diff --git a/test/unit/marked.test.js b/test/unit/marked.test.js index b604c0103e..07e58f8578 100644 --- a/test/unit/marked.test.js +++ b/test/unit/marked.test.js @@ -396,6 +396,32 @@ describe('marked unit', () => { assert.strictEqual(html, '

extension1 RENDERER EXTENSION

\n
extension2 TOKENIZER EXTENSION\n
\n'); }); + it('should fall back to the default checkbox renderer if the extension returns false', () => { + marked.use({ + extensions: [{ + name: 'checkbox', + renderer(token) { + return token.checked ? false : ' '; + }, + }], + }); + const html = marked.parse('- [x] one\n- [ ] two\n'); + assert.strictEqual(html, '
    \n
  • one
  • \n
  • two
  • \n
\n'); + }); + + it('should fall back to the default checkbox renderer in a loose list if the extension returns false', () => { + marked.use({ + extensions: [{ + name: 'checkbox', + renderer() { + return false; + }, + }], + }); + const html = marked.parse('- [x] one\n\n- [ ] two\n'); + assert.strictEqual(html, '
    \n
  • one

    \n
  • \n
  • two

    \n
  • \n
\n'); + }); + it('should walk only specified child tokens', () => { const walkableDescription = { extensions: [{