johackim/gatsby-remark-obsidian

View on GitHub
index.js

Summary

Maintainability
A
0 mins
Test Coverage
const fs = require('fs');
const visit = require('unist-util-visit');
const Remark = require('remark');
const toString = require('mdast-util-to-string');
const breaks = require('remark-breaks');
const slugify = require('slugify');
const { createMdxAstCompiler } = require('@mdx-js/mdx');

const defaultTitleToURL = (title) => `/${slugify(title, { lower: true })}`;

const removeFrontmatter = (content) => content.replace(/^---[\s\S]+?---/, '');

const compiler = createMdxAstCompiler({ remarkPlugins: [] });

const remark = new Remark().use(breaks);

const plugin = ({ markdownAST }, options = {}) => {
    const { titleToURL = defaultTitleToURL, stripBrackets = true, highlightClassName = '', markdownFolder = '' } = options;

    visit(markdownAST, 'linkReference', (node, index, parent) => {
        const siblings = parent.children;
        const previous = siblings[index - 1];
        const next = siblings[index + 1];

        const isEmbed = previous?.value?.includes('![');

        if (!previous?.value?.includes('[', '![') || !next?.value?.includes(']')) {
            return;
        }

        previous.value = previous.value.replace('![', '');
        previous.value = previous.value.replace('[', '');
        next.value = next.value.replace(']', '');

        let heading = '';
        node.type = 'link';

        if (node.label.match(/#/)) {
            [node.children[0].value, heading] = node.label.split('#');
            [heading] = heading.split('|');
            node.label = node.label.replace(`#${heading}`, '');
        }

        if (node.label.match(/\|/)) {
            [node.label, node.children[0].value] = node.label.split('|');
        }

        node.url = `${titleToURL(node.label)}${heading && `#${slugify(heading, { lower: true })}`}`;
        node.title = node.label;

        if (!stripBrackets) {
            node.children[0].value = `[[${node.children[0].value}]]`;
        }

        if (isEmbed && markdownFolder) {
            const filePath = `${markdownFolder}/${node.title}.md`;

            if (fs.existsSync(filePath)) {
                const content = removeFrontmatter(fs.readFileSync(filePath, 'utf8'));
                const embedAst = plugin({ markdownAST: compiler.parse(content) }, options);
                parent.children.splice(index, 1);
                embedAst.children.map((children) => parent.children.push(children));
            }
        }

        delete node.label;
        delete node.referenceType;
        delete node.identifier;
    });

    visit(markdownAST, 'paragraph', (node, index, parent) => {
        const text = toString(node);

        const internalLinkRegex = /!?\[\[([a-zA-Z-'À-ÿ|# ]+)\]\]/;

        if (text.match(internalLinkRegex)) {
            const isEmbed = text.includes('![');

            let label = text.match(internalLinkRegex)[0].replace(/!?\[|\]/g, '');
            let heading = '';

            if (label.match(/#/)) {
                [, heading] = label.split('#');
                [heading] = heading.split('|');
                label = label.replace(`#${heading}`, '');
            }

            let url = titleToURL(label);

            if (label.match(/\|/)) {
                [url, label] = label.split('|');
                url = titleToURL(url);
            }

            url = `${url}${heading && `#${slugify(heading, { lower: true })}`}`;
            const aHref = `<a href="${url}" title="${label}">${label}</a>`;
            const html = text.replace(internalLinkRegex, aHref);

            if (isEmbed && markdownFolder) {
                const filePath = `${markdownFolder}/${label}.md`;

                if (fs.existsSync(filePath)) {
                    const content = removeFrontmatter(fs.readFileSync(filePath, 'utf8'));
                    const embedAst = plugin({ markdownAST: remark.parse(content) }, options);
                    parent.children.splice(index, 1);
                    parent.children.splice(index, 0, ...embedAst.children);
                }
            }

            node.type = 'html';
            node.children = undefined;
            node.value = html;
        }
    });

    visit(markdownAST, 'paragraph', (node) => {
        const paragraph = toString(node);

        const highlightRegex = /==(.*)==/g;

        if (paragraph.match(highlightRegex)) {
            const html = paragraph.replace(highlightRegex, `<mark className="${highlightClassName}">$1</mark>`);
            node.type = 'html';
            node.children = undefined;
            node.value = `<p>${html}</p>`;
        }
    });

    return markdownAST;
};

module.exports = plugin;