diff --git a/.circleci/config.yml b/.circleci/config.yml index d02402927..fb239ceb3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,9 +64,6 @@ jobs: - run: name: Test - Mustache Spans command: npm run test:mustache-syntax - - run: - name: Test - Definition Lists - command: npm run test:definition-lists - run: name: Test - Hard Breaks command: npm run test:hard-breaks diff --git a/package-lock.json b/package-lock.json index 82c599f7b..ddf8d7138 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "lodash": "^4.17.21", "marked": "15.0.8", "marked-alignment-paragraphs": "^1.0.0", + "marked-definition-lists": "^1.0.1", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", @@ -10006,6 +10007,15 @@ "marked": ">=3 <16" } }, + "node_modules/marked-definition-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/marked-definition-lists/-/marked-definition-lists-1.0.1.tgz", + "integrity": "sha512-+fBAbGbVnGNNkCLMTnieReZ+gq14iCF0rBc1BBMGDDqnKeImxdvoV9kB8xRTVW4ZH5BPp6XLVykUZW0icsvZTw==", + "license": "MIT", + "peerDependencies": { + "marked": ">=3 <16" + } + }, "node_modules/marked-emoji": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-2.0.0.tgz", diff --git a/package.json b/package.json index 43b239e0c..8b817cfd3 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "test:mustache-syntax:inline": "jest \".*(mustache-syntax).*\" -t '^Inline:.*' --verbose --noStackTrace", "test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace", "test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace", - "test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace", "test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace", "test:non-breaking-spaces": "jest tests/markdown/non-breaking-spaces.test.js --verbose --noStackTrace", "test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace", @@ -108,6 +107,7 @@ "less": "^3.13.1", "lodash": "^4.17.21", "marked": "15.0.8", + "marked-definition-lists": "^1.0.1", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index a9740652b..90a9488dc 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -4,12 +4,13 @@ import _ from 'lodash'; import { Parser as MathParser } from 'expr-eval'; import { marked as Marked } from 'marked'; import MarkedExtendedTables from 'marked-extended-tables'; +import MarkedDefinitionLists from 'marked-definition-lists'; +import MarkedAlignedParagraphs from 'marked-alignment-paragraphs'; +import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces'; +import MarkedSubSuperText from 'marked-subsuper-text'; import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite'; import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id'; import { markedEmoji as MarkedEmojis } from 'marked-emoji'; -import MarkedAlignedParagraphs from 'marked-alignment-paragraphs'; -import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces'; -import MarkedSubSuperText from 'marked-subsuper-text'; import { romanize } from 'romans'; import writtenNumber from 'written-number'; @@ -410,93 +411,6 @@ const forcedParagraphBreaks = { } }; -const definitionListsSingleLine = { - name : 'definitionListsSingleLine', - level : 'block', - start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match - tokenizer(src, tokens) { - const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; - let match; - let endIndex = 0; - const definitions = []; - while (match = regex.exec(src)) { - const originalLine = match[0]; // This line and below to handle conflict with emojis - let firstLine = originalLine; // Remove in V4 when definitionListsInline updated to - this.lexer.inlineTokens(firstLine.trim()) // require spaces around `::` - .filter((t)=>t.type == 'emoji') - .map((emoji)=>firstLine = firstLine.replace(emoji.raw, 'x'.repeat(emoji.raw.length))); - - const newMatch = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym.exec(firstLine); - if(newMatch) { - definitions.push({ - dt : this.lexer.inlineTokens(originalLine.slice(0, newMatch[1].length).trim()), - dd : this.lexer.inlineTokens(originalLine.slice(newMatch[1].length + 2).trim()) - }); - } // End of emoji hack. - endIndex = regex.lastIndex; - } - if(definitions.length) { - return { - type : 'definitionListsSingleLine', - raw : src.slice(0, endIndex), - definitions - }; - } - }, - renderer(token) { - return `
${token.definitions.reduce((html, def)=>{ - return `${html}
${this.parser.parseInline(def.dt)}
` - + `
${this.parser.parseInline(def.dd)}
\n`; - }, '')}
`; - } -}; - -const definitionListsMultiLine = { - name : 'definitionListsMultiLine', - level : 'block', - start(src) { return src.match(/\n[^\n]*\n::[^:\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match - tokenizer(src, tokens) { - const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::[^:\n]))|\n::([^:\n](?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y; - let match; - let endIndex = 0; - 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

- break; - definitions.push({ - dt : this.lexer.inlineTokens(match[1].trim()), - dds : [] - }); - } - if(match[2] && definitions.length) { - definitions[definitions.length - 1].dds.push( - this.lexer.inlineTokens(match[2].trim().replace(/\s/g, ' ')) - ); - } - endIndex = regex.lastIndex; - } - if(definitions.length) { - return { - type : 'definitionListsMultiLine', - raw : src.slice(0, endIndex), - definitions - }; - } - }, - renderer(token) { - let returnVal = `

`; - token.definitions.forEach((def)=>{ - const dds = def.dds.map((s)=>{ - return `\n
${this.parser.parseInline(s).trim()}
`; - }).join(''); - returnVal += `
${this.parser.parseInline(def.dt)}
${dds}\n`; - }); - returnVal = returnVal.trim(); - return `${returnVal}
`; - } -}; - //v=====--------------------< Variable Handling >-------------------=====v// 242 lines const replaceVar = function(input, hoist=false, allowUnresolved=false) { const regex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g; @@ -765,8 +679,8 @@ const tableTerminators = [ ]; Marked.use(MarkedVariables()); -Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, - mustacheSpans, mustacheDivs, mustacheInjectInline] }); +Marked.use(MarkedDefinitionLists()); +Marked.use({ extensions : [forcedParagraphBreaks, mustacheSpans, mustacheDivs, mustacheInjectInline] }); Marked.use(mustacheInjectBlock); Marked.use(MarkedAlignedParagraphs()); Marked.use(MarkedSubSuperText());