plugins_link.js

/**
 * @module plugins/link
 */
import util from '../util.js';

/**
 * MarkdownIt plugin to handle links
 * 
 * @param {MarkdownIt} md - A `MarkdownIt` instance
 */
function plugin(md) {
    md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
        const page = localPage(tokens, idx, env, md);
        if (page) {
            return confluenceLinkOpen(page);
        }
        return self.renderToken(tokens, idx, options);
    };

    md.renderer.rules.link_close = (tokens, idx, options, env, self) => {
        // Links are parsed as 3 (or more) tokens [link_open],[text],[link_close]
        // With idx in this context referring to [link_close] we backtrack
        // To pick the related [link_open]
        let linkOpenIdx = idx - 1;
        while (tokens[linkOpenIdx].type !== 'link_open' && linkOpenIdx > -1) {
            linkOpenIdx--;
        }

        if (tokens[linkOpenIdx].type === 'link_open' && localPage(tokens, linkOpenIdx, env, md)) {
            return confluenceLinkClose();
        }
        return self.renderToken(tokens, idx, options);
    };
}

/**
 * Lookup the title of the page the link token refers by path
 * 
 * @param {Array<Token>} tokens - Array of parsed tokens
 * @param {number} idx - Current token index
 * @param {object} param2 - Parser env object
 * @param {MarkdownIt} md - A `MarkdownIt` instance
 * @returns {string|undefined} The title of the page if exists in `pageRefs`
 */
function localPage(tokens, idx, { page, pageRefs }, md) {
    const link = tokens[idx];
    const attrs = Object.fromEntries(link.attrs);
    const href = md.utils.escapeHtml(attrs.href);
    if (isLocal(href)) {
        const relPath = util.safePath(href, page?.path);
        if (relPath && pageRefs && pageRefs[relPath]) {
            return pageRefs[relPath];
        }
    }
}
/**
 * 
 * @param {string} href - The `href` attribute of an image token
 * @returns {string} `true` if the `href` does not start with `http`
 */
function isLocal(href) {
    return !href.toLowerCase().startsWith('http');
}

/**
 * Opening markup for confluence link
 * 
 * @param {string} title - Page title
 * @returns {string} Html markup
 */
function confluenceLinkOpen(title) {
    return `<ac:link ac:card-appearance="inline"><ri:page ri:content-title="${title}" /><ac:link-body>`;
}

/**
 * Closing markup for confluence link
 * 
 * @returns {string} Html markup
 */
function confluenceLinkClose() {
    return '</ac:link-body></ac:link>';
}

export default plugin;