113 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
						|
import MarkdownIt from 'markdown-it';
 | 
						|
import { parseHeader } from '../utils/parseHeader';
 | 
						|
import { highlight } from './plugins/highlight';
 | 
						|
import { slugify } from './plugins/slugify';
 | 
						|
import { highlightLinePlugin } from './plugins/highlightLines';
 | 
						|
import { lineNumberPlugin } from './plugins/lineNumbers';
 | 
						|
import { componentPlugin } from './plugins/component';
 | 
						|
import { containerPlugin } from './plugins/containers';
 | 
						|
import { snippetPlugin } from './plugins/snippet';
 | 
						|
import { hoistPlugin } from './plugins/hoist';
 | 
						|
import { preWrapperPlugin } from './plugins/preWrapper';
 | 
						|
import { linkPlugin } from './plugins/link';
 | 
						|
import { extractHeaderPlugin } from './plugins/header';
 | 
						|
import type { Header } from '../../shared';
 | 
						|
 | 
						|
const emoji = require('markdown-it-emoji');
 | 
						|
const anchor = require('markdown-it-anchor');
 | 
						|
const toc = require('markdown-it-table-of-contents');
 | 
						|
 | 
						|
export interface MarkdownOptions extends MarkdownIt.Options {
 | 
						|
  lineNumbers?: boolean;
 | 
						|
  config?: (md: MarkdownIt) => void;
 | 
						|
  anchor?: {
 | 
						|
    permalink?: boolean;
 | 
						|
    permalinkBefore?: boolean;
 | 
						|
    permalinkSymbol?: string;
 | 
						|
  };
 | 
						|
  // https://github.com/Oktavilla/markdown-it-table-of-contents
 | 
						|
  toc?: any;
 | 
						|
  externalLinks?: Record<string, string>;
 | 
						|
}
 | 
						|
 | 
						|
export interface MarkdownParsedData {
 | 
						|
  hoistedTags?: string[];
 | 
						|
  links?: string[];
 | 
						|
  headers?: Header[];
 | 
						|
  vueCode?: string;
 | 
						|
}
 | 
						|
 | 
						|
export interface MarkdownRenderer {
 | 
						|
  __data: MarkdownParsedData;
 | 
						|
  render: (src: string, env?: any) => { html: string; data: any };
 | 
						|
}
 | 
						|
 | 
						|
export const createMarkdownRenderer = (options: MarkdownOptions = {}): MarkdownRenderer => {
 | 
						|
  const md = MarkdownIt({
 | 
						|
    html: true,
 | 
						|
    linkify: true,
 | 
						|
    highlight,
 | 
						|
    ...options,
 | 
						|
  });
 | 
						|
 | 
						|
  // custom plugins
 | 
						|
  md.use(componentPlugin)
 | 
						|
    .use(highlightLinePlugin)
 | 
						|
    .use(preWrapperPlugin)
 | 
						|
    .use(snippetPlugin)
 | 
						|
    .use(hoistPlugin)
 | 
						|
    .use(containerPlugin)
 | 
						|
    .use(extractHeaderPlugin)
 | 
						|
    .use(linkPlugin, {
 | 
						|
      target: '_blank',
 | 
						|
      rel: 'noopener noreferrer',
 | 
						|
      ...options.externalLinks,
 | 
						|
    })
 | 
						|
 | 
						|
    // 3rd party plugins
 | 
						|
    .use(emoji)
 | 
						|
    .use(anchor, {
 | 
						|
      slugify,
 | 
						|
      permalink: anchor.permalink.linkInsideHeader({
 | 
						|
        symbol: `
 | 
						|
          <span aria-hidden="true" class="anchor">#</span>
 | 
						|
        `,
 | 
						|
      }),
 | 
						|
      permalinkBefore: true,
 | 
						|
      permalinkSymbol: '#',
 | 
						|
      permalinkAttrs: () => ({ 'aria-hidden': true }),
 | 
						|
      tabIndex: false,
 | 
						|
      ...options.anchor,
 | 
						|
    })
 | 
						|
    .use(toc, {
 | 
						|
      slugify,
 | 
						|
      includeLevel: [2, 3],
 | 
						|
      format: parseHeader,
 | 
						|
      ...options.toc,
 | 
						|
    });
 | 
						|
 | 
						|
  // apply user config
 | 
						|
  if (options.config) {
 | 
						|
    options.config(md);
 | 
						|
  }
 | 
						|
 | 
						|
  if (options.lineNumbers) {
 | 
						|
    md.use(lineNumberPlugin);
 | 
						|
  }
 | 
						|
 | 
						|
  // wrap render so that we can return both the html and extracted data.
 | 
						|
  const render = md.render;
 | 
						|
  const wrappedRender: MarkdownRenderer['render'] = src => {
 | 
						|
    (md as any).__data = {};
 | 
						|
    const html = render.call(md, src);
 | 
						|
    return {
 | 
						|
      html,
 | 
						|
      data: (md as any).__data,
 | 
						|
    };
 | 
						|
  };
 | 
						|
  (md as any).render = wrappedRender;
 | 
						|
 | 
						|
  return md as any;
 | 
						|
};
 |