undergroundwires/privacy.sexy

View on GitHub
src/presentation/components/Scripts/View/Tree/NodeContent/Markdown/Renderers/MarkdownItHtmlRenderer.ts

Summary

Maintainability
A
0 mins
Test Coverage
import MarkdownIt from 'markdown-it';
import type { MarkdownRenderer } from '../MarkdownRenderer';
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'; // eslint-disable-line import/extensions

export class MarkdownItHtmlRenderer implements MarkdownRenderer {
  public render(markdownContent: string): string {
    const markdownParser = new MarkdownIt({
      html: true, // Enable HTML tags in source to allow other custom rendering logic.
      linkify: false, // Disables auto-linking; handled manually for custom formatting.
      breaks: false, // Disables conversion of single newlines (`\n`) to HTML breaks (`<br>`).
    });
    configureLinksToOpenInNewTab(markdownParser);
    return markdownParser.render(markdownContent);
  }
}

function configureLinksToOpenInNewTab(markdownParser: MarkdownIt): void {
  // https://github.com/markdown-it/markdown-it/blob/14.0.0/docs/architecture.md#renderer
  const defaultLinkRenderer = getDefaultRenderer(markdownParser, 'link_open');
  markdownParser.renderer.rules.link_open = (tokens, index, options, env, self) => {
    const currentToken = tokens[index];
    Object.entries(AnchorAttributesForExternalLinks).forEach(([attribute, value]) => {
      currentToken.attrSet(attribute, value);
    });
    return defaultLinkRenderer(tokens, index, options, env, self);
  };
}

function getDefaultRenderer(md: MarkdownIt, ruleName: string): RenderRule {
  const ruleRenderer = md.renderer.rules[ruleName];
  const renderTokenAsDefault: RenderRule = (tokens, idx, options, _env, self) => {
    return self.renderToken(tokens, idx, options);
  };
  return ruleRenderer || renderTokenAsDefault;
}

const AnchorAttributesForExternalLinks: Record<string, string> = {
  target: '_blank',
  rel: 'noopener noreferrer',
} as const;