aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorache <ache@ache.one>2018-10-22 20:08:32 +0200
committerache <ache@ache.one>2018-10-22 20:08:32 +0200
commite6d1f94d29cd2d3186b38f74347a49ac43ef3ca3 (patch)
tree9aa63666097fd8e07f74863a50242ffdb404ac34
parentRefactoring 💕 (diff)
parentoptional mdAttr configuration (diff)
Merge branch 'master' of github.com:arobase-che/remark-attr
-rw-r--r--__tests__/index.js57
-rw-r--r--src/index.js97
2 files changed, 152 insertions, 2 deletions
diff --git a/__tests__/index.js b/__tests__/index.js
index 355bc80..f96acd4 100644
--- a/__tests__/index.js
+++ b/__tests__/index.js
@@ -147,3 +147,60 @@ test('global-aria', t => {
t.deepEqual(parse(contents), parse('<p> <em>Wait</em> ! I <strong style="color: pink;" aria-love="true">love</strong> you!</p>'));
});
+test('fenced code', t => {
+ const fencedCodeString = `~~~lang info=string
+This is an awesome code
+
+~~~
+`;
+ const {contents} = render(fencedCodeString);
+ t.deepEqual(parse(contents), parse(`<pre><code class="language-lang" info="string">This is an awesome code
+</code></pre>`));
+});
+
+test('fenced code brackets', t => {
+ const fencedCodeString = `~~~lang{info=string}
+This is an awesome code
+
+~~~
+`;
+ const {contents} = render(fencedCodeString);
+ t.deepEqual(parse(contents), parse(`<pre><code class="language-lang" info="string">This is an awesome code
+</code></pre>`));
+});
+
+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(`<pre><code class="language-lang" info="string">This is an awesome code
+</code></pre>`));
+});
+
+test('fenced code fallback', t => {
+ const fallbackFCstring = `~~~lang
+This is an awesome code
+
+~~~
+{info=string}
+`;
+ const {contents} = render(fallbackFCstring);
+ t.deepEqual(parse(contents), parse(`<pre><code class="language-lang" info="string">This is an awesome code
+</code></pre>`));
+});
+
+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(`<pre><code class="language-lang" info="string">This is an awesome code
+</code></pre>`));
+});
+
diff --git a/src/index.js b/src/index.js
index d9b563a..49ee990 100644
--- a/src/index.js
+++ b/src/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 = {};
const DOMEventHandler = require('./dom-event-handler.js');
@@ -17,6 +20,7 @@ const convTypeTag = {
emphasis: 'em',
delete: 's',
inlineCode: 'code',
+ code: 'code',
'*': '*',
};
@@ -60,7 +64,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
@@ -152,6 +156,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, config.mdAttrConfig);
+ }
+ }
+
+ 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, config.mdAttrConfig)};
+ 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;
@@ -165,6 +254,7 @@ function remarkAttr(userConfig) {
elements: supportedElements,
extend: {},
scope: 'extended',
+ mdAttrConfig: undefined,
};
const config = {...defaultConfig, ...userConfig};
@@ -181,6 +271,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);