@photogabble/eleventy-plugin-interlinker
Version:
Obsidian WikiLinks, BackLinks and Embed support for 11ty
95 lines (78 loc) • 3.45 kB
JavaScript
/**
* This rule will be looped through an inline token by markdown-it.
*
* @param {WikilinkParser} wikilinkParser
* @returns {(function(*, *): (boolean|undefined))|*}
*/
export const wikilinkInlineRule = (wikilinkParser) => (state, silent) => {
// Have we found the start of a WikiLink Embed `![[`
if (state.src.charAt(state.pos) === '[' && state.src.charAt(state.pos + 1) !== '[') return false; // Not wikilink opening
if (state.src.charAt(state.pos) === '!' && state.src.substring(state.pos, state.pos + 3) !== '![[') return false; // Not embed opening
const matches = state.src.match(wikilinkParser.wikiLinkRegExp);
if (!matches) return false;
// We have found the start of a WikiLink (`[[`) or Embed (`![[`)
// char at state.pos will be either `[` or `!`, need to walk through state.src until `]]` is encountered
let pos = state.pos;
let text = '';
let found = false;
while (pos <= state.posMax) {
text += state.src.charAt(pos);
if (text.length > 2 && text.substring(text.length - 2) === ']]') {
found = true;
break;
}
pos++;
}
if (!found) return false;
const wikiLink = wikilinkParser.linkCache.get(text);
// By this time in the execution cycle the wikilink parser's cache should contain all
// wikilinks, including those linking to a stub. In the unlikely case that it doesn't
// we ignore the wikilink.
if (!wikiLink) return false;
if (!silent) {
// The wikilink content is HTML generated by a "Resolving Function" lets make use of
// the builtin html_inline render rule to display it.
const token = state.push('html_inline', '', 0);
token.content = wikiLink.content.trim();
}
state.pos = state.pos + text.length;
return true;
};
/**
* This rule is to catch Wikilink Embeds that are bookended by new lines e.g `\n![[test]]\n`, the inline rule
* would only get the content within the new lines with the newlines themselves getting tokenised as a paragraph.
* That was causing the bugs raised in issues #65 and #66.
*
* @param wikilinkParser
* @return {(function(*, *, *, *): boolean)|*}
*/
export const wikilinkBlockRule = (wikilinkParser) => (state, startLine, endLine, silent) => {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
// This block rule only cares about Wiki Link embeds that are at a block level (on a single line bookended by `\n`)
// therefore if the line text doesn't begin with `![[` we do not continue.
let lineText = state.src.slice(pos, max);
if (lineText.substring(0, 3) !== '![[') return false;
const wikiLink = wikilinkParser.linkCache.get(lineText);
if (!wikiLink) return false;
if (!silent) {
const token = state.push('html_block', '', 0);
token.content = wikiLink.content.trim();
}
// Block level Wikilink embeds should be on a single line, increment the line counter and continue.
state.line++;
return true;
};
export const install = (md, wikilinkParser) => {
md.inline.ruler.push('inline_wikilink', wikilinkInlineRule(
wikilinkParser,
));
md.block.ruler.before('heading', 'block_wikilink', wikilinkBlockRule(
wikilinkParser,
), {
// alt contains a list of rules which can be terminated by this one
alt: ['paragraph', 'reference', 'blockquote']
});
}