@mapbox/jsxtreme-markdown
Version:
Transform Markdown into JSX or React component modules
368 lines (299 loc) • 7.4 kB
JavaScript
'use strict';
var whitespace = require('is-whitespace-character');
var locate = require('../locate/link');
module.exports = link;
link.locator = locate;
var lineFeed = '\n';
var exclamationMark = '!';
var quotationMark = '"';
var apostrophe = "'";
var leftParenthesis = '(';
var rightParenthesis = ')';
var lessThan = '<';
var greaterThan = '>';
var leftSquareBracket = '[';
var backslash = '\\';
var rightSquareBracket = ']';
var graveAccent = '`';
function link(eat, value, silent) {
var self = this;
var subvalue = '';
var index = 0;
var character = value.charAt(0);
var pedantic = self.options.pedantic;
var commonmark = self.options.commonmark;
var gfm = self.options.gfm;
var closed;
var count;
var opening;
var beforeURL;
var beforeTitle;
var subqueue;
var hasMarker;
var isImage;
var content;
var marker;
var length;
var title;
var depth;
var queue;
var url;
var now;
var exit;
var node;
// Detect whether this is an image.
if (character === exclamationMark) {
isImage = true;
subvalue = character;
character = value.charAt(++index);
}
// Eat the opening.
if (character !== leftSquareBracket) {
return;
}
// Exit when this is a link and we’re already inside a link.
if (!isImage && self.inLink) {
return;
}
subvalue += character;
queue = '';
index++;
// Eat the content.
length = value.length;
now = eat.now();
depth = 0;
now.column += index;
now.offset += index;
while (index < length) {
character = value.charAt(index);
subqueue = character;
if (character === graveAccent) {
// Inline-code in link content.
count = 1;
while (value.charAt(index + 1) === graveAccent) {
subqueue += character;
index++;
count++;
}
if (!opening) {
opening = count;
} else if (count >= opening) {
opening = 0;
}
} else if (character === backslash) {
// Allow brackets to be escaped.
index++;
subqueue += value.charAt(index);
} else if ((!opening || gfm) && character === leftSquareBracket) {
// In GFM mode, brackets in code still count. In all other modes,
// they don’t.
depth++;
} else if ((!opening || gfm) && character === rightSquareBracket) {
if (depth) {
depth--;
} else {
if (value.charAt(index + 1) !== leftParenthesis) {
return;
}
subqueue += leftParenthesis;
closed = true;
index++;
break;
}
}
queue += subqueue;
subqueue = '';
index++;
}
// Eat the content closing.
if (!closed) {
return;
}
content = queue;
subvalue += queue + subqueue;
index++;
// Eat white-space.
while (index < length) {
character = value.charAt(index);
if (!whitespace(character)) {
break;
}
subvalue += character;
index++;
}
// Eat the URL.
character = value.charAt(index);
queue = '';
beforeURL = subvalue;
if (character === lessThan) {
index++;
beforeURL += lessThan;
while (index < length) {
character = value.charAt(index);
if (character === greaterThan) {
break;
}
if (commonmark && character === lineFeed) {
return;
}
queue += character;
index++;
}
if (value.charAt(index) !== greaterThan) {
return;
}
subvalue += lessThan + queue + greaterThan;
url = queue;
index++;
} else {
character = null;
subqueue = '';
while (index < length) {
character = value.charAt(index);
if (
subqueue &&
(character === quotationMark ||
character === apostrophe ||
(commonmark && character === leftParenthesis))
) {
break;
}
if (whitespace(character)) {
if (!pedantic) {
break;
}
subqueue += character;
} else {
if (character === leftParenthesis) {
depth++;
} else if (character === rightParenthesis) {
if (depth === 0) {
break;
}
depth--;
}
queue += subqueue;
subqueue = '';
if (character === backslash) {
queue += backslash;
character = value.charAt(++index);
}
queue += character;
}
index++;
}
subvalue += queue;
url = queue;
index = subvalue.length;
}
// Eat white-space.
queue = '';
while (index < length) {
character = value.charAt(index);
if (!whitespace(character)) {
break;
}
queue += character;
index++;
}
character = value.charAt(index);
subvalue += queue;
// Eat the title.
if (
queue &&
(character === quotationMark ||
character === apostrophe ||
(commonmark && character === leftParenthesis))
) {
index++;
subvalue += character;
queue = '';
marker = character === leftParenthesis ? rightParenthesis : character;
beforeTitle = subvalue;
// In commonmark-mode, things are pretty easy: the marker cannot occur
// inside the title. Non-commonmark does, however, support nested
// delimiters.
if (commonmark) {
while (index < length) {
character = value.charAt(index);
if (character === marker) {
break;
}
if (character === backslash) {
queue += backslash;
character = value.charAt(++index);
}
index++;
queue += character;
}
character = value.charAt(index);
if (character !== marker) {
return;
}
title = queue;
subvalue += queue + character;
index++;
while (index < length) {
character = value.charAt(index);
if (!whitespace(character)) {
break;
}
subvalue += character;
index++;
}
} else {
subqueue = '';
while (index < length) {
character = value.charAt(index);
if (character === marker) {
if (hasMarker) {
queue += marker + subqueue;
subqueue = '';
}
hasMarker = true;
} else if (!hasMarker) {
queue += character;
} else if (character === rightParenthesis) {
subvalue += queue + marker + subqueue;
title = queue;
break;
} else if (whitespace(character)) {
subqueue += character;
} else {
queue += marker + subqueue + character;
subqueue = '';
hasMarker = false;
}
index++;
}
}
}
if (value.charAt(index) !== rightParenthesis) {
return;
}
/* istanbul ignore if - never used (yet) */
if (silent) {
return true;
}
subvalue += rightParenthesis;
url = self.decode.raw(self.unescape(url), eat(beforeURL).test().end, {
nonTerminated: false,
});
if (title) {
beforeTitle = eat(beforeTitle).test().end;
title = self.decode.raw(self.unescape(title), beforeTitle);
}
node = {
type: isImage ? 'image' : 'link',
title: title || null,
url: url,
};
if (isImage) {
node.alt = self.decode.raw(self.unescape(content), now) || null;
} else {
exit = self.enterLink();
node.children = self.tokenizeInline(content, now);
exit();
}
return eat(subvalue)(node);
}