diff --git a/package-lock.json b/package-lock.json index add91f28e..a067fc932 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "marked-emoji": "^1.4.3", "marked-extended-tables": "^1.1.0", "marked-gfm-heading-id": "^4.0.1", + "marked-nonbreaking-spaces": "file:../marked-nonbreaking-spaces", "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", @@ -73,6 +74,36 @@ "npm": "^10.2.x" } }, + "../marked-nonbreaking-spaces": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", + "@markedjs/testutils": "^15.0.0-0", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-node-resolve": "^16.0.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^11.0.1", + "@semantic-release/npm": "^12.0.1", + "@semantic-release/release-notes-generator": "^14.0.2", + "babel-jest": "^29.5.0", + "eslint": "^8.57.1", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.6.0", + "jest-cli": "^29.7.0", + "marked": "^15.0.4", + "rollup": "^4.29.2", + "semantic-release": "^24.2.0" + }, + "peerDependencies": { + "marked": ">=3 <16" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -9874,6 +9905,10 @@ "marked": ">=13 <16" } }, + "node_modules/marked-nonbreaking-spaces": { + "resolved": "../marked-nonbreaking-spaces", + "link": true + }, "node_modules/marked-smartypants-lite": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/marked-smartypants-lite/-/marked-smartypants-lite-1.0.3.tgz", diff --git a/package.json b/package.json index 0097e5313..c36c71bba 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "marked-emoji": "^1.4.3", "marked-extended-tables": "^1.1.0", "marked-gfm-heading-id": "^4.0.1", + "marked-nonbreaking-spaces": "file:../marked-nonbreaking-spaces", "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 99766b536..6fc03b379 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -7,6 +7,7 @@ import MarkedExtendedTables from 'marked-extended-tables'; 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 MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces'; //Icon fonts included so they can appear in emoji autosuggest dropdown import diceFont from '../../themes/fonts/iconFonts/diceFont.js'; @@ -419,27 +420,6 @@ const forcedParagraphBreaks = { } }; -const nonbreakingSpaces = { - name : 'nonbreakingSpaces', - level : 'inline', - start(src) { return src.match(/:>+/m)?.index; }, // Hint to Marked.js to stop and check for a match - tokenizer(src, tokens) { - const regex = /:(>+)/ym; - const match = regex.exec(src); - if(match?.length) { - return { - type : 'nonbreakingSpaces', // Should match "name" above - raw : match[0], // Text to consume from the source - length : match[1].length, - text : '' - }; - } - }, - renderer(token) { - return ` `.repeat(token.length).concat(''); - } -}; - const definitionListsSingleLine = { name : 'definitionListsSingleLine', level : 'block', @@ -796,8 +776,9 @@ const tableTerminators = [ Marked.use(MarkedVariables()); Marked.use({ extensions : [justifiedParagraphs, definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, - nonbreakingSpaces, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); + superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); Marked.use(mustacheInjectBlock); +Marked.use(MarkedNonbreakingSpaces()); Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false }); Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); diff --git a/tests/markdown/non-breaking-spaces.test.js b/tests/markdown/non-breaking-spaces.test.js index 9dad4eb0f..1cc1fe6da 100644 --- a/tests/markdown/non-breaking-spaces.test.js +++ b/tests/markdown/non-breaking-spaces.test.js @@ -3,54 +3,7 @@ import Markdown from 'naturalcrit/markdown.js'; describe('Non-Breaking Spaces', ()=>{ - test('Single Space', function() { - const source = ':>\n\n'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

 

`); - }); - - test('Double Space', function() { - const source = ':>>\n\n'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  

`); - }); - - test('Triple Space', function() { - const source = ':>>>\n\n'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   

`); - }); - - test('Many Space', function() { - const source = ':>>>>>>>>>>\n\n'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

          

`); - }); - - test('Multiple sets of Spaces', function() { - const source = ':>>>\n:>>>\n:>>>'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n   \n   

`); - }); - - test('Pair of inline Spaces', function() { - const source = ':>>:>>'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    

`); - }); - - test('Space directly between two paragraphs', function() { - const source = 'Line 1\n:>>\nLine 2'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Line 1\n  \nLine 2

`); - }); - - test('Ignored inside a code block', function() { - const source = '```\n\n:>\n\n```\n'; - const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n:>\n
`); - }); - +// Interaction tests test('I am actually a single-line definition list!', function() { const source = 'Term ::> Definition 1\n'; const rendered = Markdown.render(source).trim();