remarkFileLinksPlugin.ts1.5 KBView on GitHub
import type { Parent, PhrasingContent, Root, Text } from 'mdast';
import { visit } from 'unist-util-visit';

const FILE_LINK_REGEX = /\[\[doc:([0-9a-fA-F-]{36})(?:\|([^\]]+))?\]\]/g;

export function remarkFileLinksPlugin() {
  return (tree: Root) => {
    visit(tree, 'text', (node: Text, index: number | undefined, parent: Parent | undefined) => {
      if (index === undefined || !parent) return;
      const value = node.value;
      if (!value.includes('[[doc:')) return;

      const newNodes: (Text | object)[] = [];
      let lastIndex = 0;
      let match: RegExpExecArray | null;
      const regex = new RegExp(FILE_LINK_REGEX.source, 'g');

      while ((match = regex.exec(value)) !== null) {
        const matchStart = match.index;
        const matchEnd = match.index + match[0].length;

        if (matchStart > lastIndex) {
          newNodes.push({ type: 'text', value: value.slice(lastIndex, matchStart) } as Text);
        }

        newNodes.push({
          type: 'fileLink',
          data: {
            hName: 'file-link',
            hProperties: {
              'data-document-id': match[1],
            },
          },
        });

        lastIndex = matchEnd;
      }

      if (lastIndex < value.length) {
        newNodes.push({ type: 'text', value: value.slice(lastIndex) } as Text);
      }

      if (newNodes.length > 1 || (newNodes.length === 1 && (newNodes[0] as Text).type !== 'text')) {
        parent.children.splice(index, 1, ...(newNodes as PhrasingContent[]));
        return index + newNodes.length;
      }
    });
  };
}