From 22727456fc89dca086e5b726064fcbcded34ed77 Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 23 Jul 2018 10:04:00 +0200 Subject: support of fenced code --- index.js | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index e49768c..fd3553e 100644 --- a/index.js +++ b/index.js @@ -3,8 +3,11 @@ const parseAttr = require('md-attr-parser'); const htmlElemAttr = require('html-element-attributes'); -const supportedElements = ['link', 'atxHeading', 'strong', 'emphasis', 'deletion', 'code', 'setextHeading']; +const supportedElements = ['link', 'atxHeading', 'strong', 'emphasis', 'deletion', 'code', 'setextHeading', 'fencedCode']; const blockElements = ['atxHeading', 'setextHeading']; +const particularElements = ['fencedCode']; + +const particularTokenize = {}; // The list of DOM Event handler const DOMEventHandler = [ @@ -40,6 +43,7 @@ const convTypeTag = { emphasis: 'em', delete: 's', inlineCode: 'code', + code: 'code', '*': '*', }; @@ -175,6 +179,91 @@ function filterAttributes(prop, config, type) { return prop; } +/* This is a special modification of the function tokenizeGenerator + * to parse the fencedCode info string and the fallback + * customAttr parser + */ +function tokenizeFencedCode(oldParser, config) { + const prefix = '\n'; + function token(eat, value, silent) { + // This we call the old tokenize + const self = this; + let eaten = oldParser.call(self, eat, value, silent); + + let index = 0; + let parsedAttr; + let parsedByCustomAttr = false; + const {length} = value; + + if (!eaten || !eaten.position) { + return undefined; + } + + const type = convTypeTag[eaten.type]; + + // First, parse the info string + // which is the 'lang' attributes of 'eaten'. + + if (eaten.lang) { + let infoPart = ''; + if (eaten.lang.indexOf(' ') >= 0) { + if (eaten.lang.indexOf('{') >= 0) { + const posStart = Math.min(eaten.lang.indexOf(' '), eaten.lang.indexOf('{')); + infoPart = eaten.lang.substr(posStart); + + if (posStart === eaten.lang.indexOf('{')) { + eaten.lang = eaten.lang.substr(0, eaten.lang.indexOf('{')) + ' ' + infoPart; + } + } else { + infoPart = eaten.lang.substr(eaten.lang.indexOf(' ')); + } + } else if (eaten.lang.indexOf('{') >= 0) { + infoPart = eaten.lang.substr(eaten.lang.indexOf('{')); + eaten.lang = eaten.lang.substr(0, eaten.lang.indexOf('{')) + ' ' + infoPart; + } + + if (infoPart) { + parsedAttr = parseAttr(infoPart, 0); + } + } + + index = eaten.position.end.offset - eaten.position.start.offset; + + // Then we check for attributes + if (index + prefix.length < length && value.charAt(index + prefix.length) === '{') { + // If any, parse it + parsedAttr = {...parsedAttr, ...parseAttr(value, index + prefix.length)}; + parsedByCustomAttr = Boolean(parsedAttr); + } + + // If parsed configure the node + if (parsedAttr) { + if (config.scope && config.scope !== 'none') { + const filtredProp = filterAttributes(parsedAttr.prop, config, type); + + if (filtredProp !== {}) { + if (eaten.data) { + eaten.data.hProperties = {...eaten.data.hProperties, ...filtredProp}; + } else { + eaten.data = {hProperties: filtredProp}; + } + } + } + if (parsedByCustomAttr) { + eaten = eat(prefix + parsedAttr.eaten)(eaten); + } + } + + return eaten; + } + + // Return the new tokenizer function + + return token; +} + +particularTokenize.fencedCode = tokenizeFencedCode; + remarkAttr.SUPPORTED_ELEMENTS = supportedElements; module.exports = remarkAttr; @@ -204,6 +293,9 @@ function remarkAttr(userConfig) { if (blockElements.indexOf(elem) >= 0) { const oldElem = tokenizersBlock[elem]; tokenizersBlock[elem] = tokenizeGenerator('\n', oldElem, config); + } else if (particularElements.indexOf(elem) >= 0) { + const oldElem = tokenizersBlock[elem]; + tokenizersBlock[elem] = particularTokenize[elem](oldElem, config); } else { const oldElem = tokenizers[elem]; const elemTokenize = tokenizeGenerator('', oldElem, config); -- cgit v1.2.3 From 5f2044d064d43046b7df64dea9270ee6cb0524eb Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 23 Jul 2018 10:04:30 +0200 Subject: test about the support of fenced code --- __tests__/index.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/__tests__/index.js b/__tests__/index.js index 199b8e4..c7f13a1 100644 --- a/__tests__/index.js +++ b/__tests__/index.js @@ -148,3 +148,60 @@ test('invalid-extend', t => { t.deepEqual(parse(contents), parse(`

Wait ! I love you!

`)); }); +test('fenced code', t => { + const fencedCodeString = `~~~lang info=string +This is an awesome code + +~~~ +`; + const {contents} = render(fencedCodeString); + t.deepEqual(parse(contents), parse(`
This is an awesome code
+
`)); +}); + +test('fenced code brackets', t => { + const fencedCodeString = `~~~lang{info=string} +This is an awesome code + +~~~ +`; + const {contents} = render(fencedCodeString); + t.deepEqual(parse(contents), parse(`
This is an awesome code
+
`)); +}); + +test('fenced code brackets and spaces', t => { + const fencedCodeString = `~~~lang {info=string} +This is an awesome code + +~~~ +`; + const {contents} = render(fencedCodeString); + t.deepEqual(parse(contents), parse(`
This is an awesome code
+
`)); +}); + +test('fenced code fallback', t => { + const fallbackFCstring = `~~~lang +This is an awesome code + +~~~ +{info=string} +`; + const {contents} = render(fallbackFCstring); + t.deepEqual(parse(contents), parse(`
This is an awesome code
+
`)); +}); + +test('fenced code mix', t => { + const fallbackFCstring = `~~~lang{info=strong} +This is an awesome code + +~~~ +{info=string} +`; + const {contents} = render(fallbackFCstring); + t.deepEqual(parse(contents), parse(`
This is an awesome code
+
`)); +}); + -- cgit v1.2.3 From c713648467ac9846737ed9655046ecd9693b34e5 Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 23 Jul 2018 10:07:58 +0200 Subject: optional mdAttr configuration --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index fd3553e..ac2274e 100644 --- a/index.js +++ b/index.js @@ -87,7 +87,7 @@ function tokenizeGenerator(prefix, oldParser, config) { // Then we check for attributes if (index + prefix.length < length && value.charAt(index + prefix.length) === '{') { // If any, parse it - parsedAttr = parseAttr(value, index + prefix.length); + parsedAttr = parseAttr(value, index + prefix.length, config.mdAttrConfig); } // If parsed configure the node @@ -223,7 +223,7 @@ function tokenizeFencedCode(oldParser, config) { } if (infoPart) { - parsedAttr = parseAttr(infoPart, 0); + parsedAttr = parseAttr(infoPart, 0, config.mdAttrConfig); } } @@ -232,7 +232,7 @@ function tokenizeFencedCode(oldParser, config) { // Then we check for attributes if (index + prefix.length < length && value.charAt(index + prefix.length) === '{') { // If any, parse it - parsedAttr = {...parsedAttr, ...parseAttr(value, index + prefix.length)}; + parsedAttr = {...parsedAttr, ...parseAttr(value, index + prefix.length, config.mdAttrConfig)}; parsedByCustomAttr = Boolean(parsedAttr); } @@ -277,6 +277,7 @@ function remarkAttr(userConfig) { elements: supportedElements, extend: {}, scope: 'extended', + mdAttrConfig: undefined, }; const config = {...defaultConfig, ...userConfig}; -- cgit v1.2.3