Repository: naturalcrit/homebrewery
Pull request #3359 (2024-03-19 17:17:06)

Fix crash for DL, disallow block tokens as DT, add test
- [x] Fixes crash for multilne DLs where DT was empty.
- [x] Disallows Block-level tokens as the DT of a multiline DL

Example:
```
{{descriptive      
Some description
}}         // invalid DT, would cause a crash
:: definiton

### Header // invalid DT
:: definition

- List     // invalid DT
:: definition

Not header // valid DT
:: definition 
```

Could any of you quickly verify that crashes are gone, and that weird use-cases of ``:: some text for hanging indent continue to work? For example, https://homebrewery.naturalcrit.com/share/k-HWy8tLJ_Gl has several `::` in unusual places to get the hanging-indent effect, and headers/etc. immediately before should still render properly.


Patch:
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index e38c31c3..939c2cc8 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -337,12 +337,14 @@ const definitionListsMultiline = {
 		const definitions = [];
 		while (match = regex.exec(src)) {
 			if(match[1]) {
+				if(this.lexer.blockTokens(match[1].trim())[0].type !== 'paragraph') // DT must not be another block-level token besides <p>
+					break;
 				definitions.push({
 					dt  : this.lexer.inlineTokens(match[1].trim()),
 					dds : []
 				});
 			}
-			if(match[2]) {
+			if(match[2] && definitions.length) {
 				definitions[definitions.length - 1].dds.push(
 					this.lexer.inlineTokens(match[2].trim().replace(/\s/g, ' '))
 				);
@@ -615,7 +617,7 @@ function MarkedVariables() {
 //^=====--------------------< Variable Handling >-------------------=====^//
 
 Marked.use(MarkedVariables());
-Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionListsInline, definitionListsMultiline, superSubScripts] });
+Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionListsMultiline, definitionListsInline, superSubScripts] });
 Marked.use(mustacheInjectBlock);
 Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
 Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());


Test patch:
diff --git a/tests/markdown/definition-lists.test.js b/tests/markdown/definition-lists.test.js
index ee791172..87ff6f61 100644
--- a/tests/markdown/definition-lists.test.js
+++ b/tests/markdown/definition-lists.test.js
@@ -76,4 +76,10 @@ describe('Multiline Definition Lists', ()=>{
 		const rendered = Markdown.render(source).trim();
 		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl><p>Paragraph</p>');
 	});
+
+	test('Block Token cannot be the Term of a multi-line definition', function() {
+		const source = '## Header\n::Definition 1 of a single-line DL\n::Definition 1 of another single-line DL';
+		const rendered = Markdown.render(source).trim();
+		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 id="header">Header</h2>\n<dl><dt></dt><dd>Definition 1 of a single-line DL</dd>\n<dt></dt><dd>Definition 1 of another single-line DL</dd>\n</dl>');
+	});
 });