All files / src/lib/markdown-it/plugins markdown-it-icons.ts

100% Statements 56/56
100% Branches 29/29
100% Functions 10/10
100% Lines 55/55

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123                  22x           9x       22x 7x     22x 6x   6x 1x   5x       22x 3x   3x 1x   2x 2x 2x 2x       22x 11x 11x   1x 1x   1x 1x   1x 1x   3x 3x           11x         22x 13x     22x     22x 46x 46x 46x   46x 7x 39x 6x 33x 3x 30x 11x 19x 6x   13x       22x 5x   5x 4x     5x 5x                   22x   42x        
// import the necessary packages
import octicons from '@primer/octicons';
import * as cheerio from 'cheerio';
// markdown-it-regexp type definitions are undefined
// @ts-ignore
import markdownItRegExp from 'markdown-it-regexp';
 
// regular expression to match the icon patterns
const ICON_REGEXP
  = /:(fa[brs]|fa-brands|fa-solid|glyphicon|octicon|octiconlight|mi[forst]|bi)-([a-z0-9-]+)~?([a-z0-9-]+)?:/;
 
// function to get the octicon icons
function getOcticonIcon(iconName: string) {
  // Indexing octicons varies based on @types/primer__octicons version
  // @ts-ignore
  return octicons[iconName] ?? null;
}
 
// handler function for glyphicon icons
const handleGlyphicon = (iconFontName: string) =>
  `<span aria-hidden="true" class="glyphicon glyphicon-${iconFontName}"></span>`;
 
// handler function for octicon icons
const handleOcticon = (iconFontName: string, iconClass?: string) => {
  const octiconIcon = getOcticonIcon(iconFontName);
  // ensure octicons are valid
  if (octiconIcon === null) {
    return '<span aria-hidden="true"></span>';
  }
  return iconClass ? octiconIcon.toSVG({ class: iconClass }) : octiconIcon.toSVG();
};
 
// handler function for light octicon icons
const handleOcticonLight = (iconFontName: string, iconClass?: string) => {
  const octiconIcon = getOcticonIcon(iconFontName);
  // ensure octicons are valid
  if (octiconIcon === null) {
    return '<span aria-hidden="true"></span>';
  }
  const octiconIconHtml = iconClass ? octiconIcon.toSVG({ class: iconClass }) : octiconIcon.toSVG();
  const $ = cheerio.load(octiconIconHtml);
  $('svg').attr('style', 'color: #fff');
  return $.html();
};
 
// handler function for material icons
const handleMaterialIcon = (iconFontType: string, iconFontName: string) => {
  let materialIconsClass = 'material-icons';
  switch (iconFontType) {
  case 'mio':
    materialIconsClass += '-outlined';
    break;
  case 'mir':
    materialIconsClass += '-round';
    break;
  case 'mis':
    materialIconsClass += '-sharp';
    break;
  case 'mit':
    materialIconsClass += '-two-tone';
    break;
  default:
  // .material-icons generates 'Filled' style icons; hence, no suffix is needed for 'mif'.
  }
  // Use .align-middle by default to vertically-align the icon with its surrounding text (if any).
  // Also, replace dashes (-) with underscores (_) to format the icon name properly.
  return `<span aria-hidden="true" class="${materialIconsClass} align-middle">`
    + `${iconFontName.split('-').join('_')}</span>`;
};
 
// handler function for font awesome icons
const handleFontAwesome = (iconFontType: string, iconFontName: string) =>
  `<span aria-hidden="true" class="${iconFontType} fa-${iconFontName}"></span>`;
 
// Handler function for Bootstrap Icons
const handleBootstrapIcon = (iconFontName: string) => `<i class="bi-${iconFontName}"></i>`;
 
// function to get the respective icon html based on the icon font type
const getIconHtml = (match: string[]) => {
  const iconFontType = match[1];
  const iconFontName = match[2];
  const iconClass = match[3];
 
  if (iconFontType === 'glyphicon') {
    return handleGlyphicon(iconFontName);
  } else if (iconFontType === 'octicon') {
    return handleOcticon(iconFontName, iconClass);
  } else if (iconFontType === 'octiconlight') {
    return handleOcticonLight(iconFontName, iconClass);
  } else if (iconFontType.startsWith('mi')) {
    return handleMaterialIcon(iconFontType, iconFontName);
  } else if (iconFontType === 'bi') {
    return handleBootstrapIcon(iconFontName);
  }
  return handleFontAwesome(iconFontType, iconFontName);
};
 
// function to process the icon string and get the icon html
const processIconString = (iconStr: string) => {
  let icon = iconStr;
 
  if (!iconStr.startsWith(':') && !iconStr.endsWith(':')) {
    icon = `:${iconStr}:`;
  }
 
  const match = icon.match(ICON_REGEXP);
  return match ? getIconHtml(match) : null;
};
 
// create a markdown-it plugin to process the icon strings in the markdown
/**
 * A Markdown-It plugin that replaces icon syntax matching `ICON_REGEXP` with corresponding HTML.
 *
 * This plugin uses a regular expression to detect icon patterns in Markdown content,
 * and transforms them into HTML using the `getIconHtml` function.
 */
const markdownItIconsPlugin = markdownItRegExp(
  ICON_REGEXP,
  (match: string[]) => getIconHtml(match),
);
 
export { markdownItIconsPlugin, processIconString };