prism.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import Prism from 'prismjs';
  2. import prismComponents from 'prismjs/components';
  3. /**
  4. * Without this, Prism will call highlightAll() automatically on page load.
  5. * We call highlightElement() when necessary so this is both unnecessary and
  6. * can lead to issues when loading a page in a background tab.
  7. *
  8. * See https://prismjs.com/docs/Prism.html#.manual
  9. */
  10. Prism.manual = true;
  11. /**
  12. * A mapping object containing all Prism languages/aliases that can be loaded using
  13. * `loadPrismLanguage`. Maps language aliases (`js`) to the full language name
  14. * (`javascript`).
  15. */
  16. const PRISM_LANGUAGE_MAP: Record<string, string> = Object.fromEntries(
  17. Object.entries(prismComponents.languages).flatMap(([lang, value]) => {
  18. if (!value.alias) {
  19. return [[lang, lang]]; // map the full language name to itself
  20. }
  21. return [
  22. [lang, lang], // map the full language name to itself
  23. ...(Array.isArray(value.alias) // map aliases to full language name
  24. ? value.alias.map(alias => [alias, lang])
  25. : [[value.alias, lang]]),
  26. ];
  27. })
  28. );
  29. // Aliases that don't already exist in Prism.js
  30. // https://prismjs.com/#supported-languages
  31. const EXTRA_LANGUAGE_ALIASES: Record<string, string> = {
  32. cc: 'cpp',
  33. erl: 'erlang',
  34. ex: 'elixir',
  35. h: 'c',
  36. m: 'objectivec',
  37. pl: 'perl',
  38. pm: 'perl',
  39. pyx: 'python',
  40. rs: 'rust',
  41. // Ruby
  42. jbuilder: 'ruby',
  43. ru: 'ruby',
  44. rake: 'ruby',
  45. // JS
  46. cjs: 'javascript',
  47. mjs: 'javascript',
  48. jsbundle: 'javascript',
  49. bundle: 'javascript',
  50. vue: 'javascript',
  51. svelte: 'javascript',
  52. };
  53. export const getPrismLanguage = (lang: string) => {
  54. const language = lang.toLowerCase();
  55. const aliased = EXTRA_LANGUAGE_ALIASES[language];
  56. return PRISM_LANGUAGE_MAP[aliased ?? language];
  57. };
  58. /**
  59. * Loads the specified Prism language (aliases like `js` for `javascript` also work).
  60. * Will log a warning if a) the language doesn't exist (unless `suppressExistenceWarning`
  61. * is true) or b) there was a problem downloading the grammar file.
  62. */
  63. export async function loadPrismLanguage(
  64. lang: string,
  65. {
  66. onError,
  67. onLoad,
  68. suppressExistenceWarning,
  69. }: {
  70. onError?: (error) => void;
  71. onLoad?: () => void;
  72. suppressExistenceWarning?: boolean;
  73. }
  74. ) {
  75. try {
  76. const language: string | undefined = getPrismLanguage(lang);
  77. // Short-circuit if language already loaded
  78. if (Prism.languages[language]) {
  79. onLoad?.();
  80. return;
  81. }
  82. // If Prism doesn't have any grammar file available for the language
  83. if (!language) {
  84. if (!suppressExistenceWarning) {
  85. // eslint-disable-next-line no-console
  86. console.warn(
  87. `No Prism grammar file for \`${lang}\` exists. Check the \`lang\` argument passed to \`loadPrismLanguage()\`.`
  88. );
  89. }
  90. return;
  91. }
  92. // Check for dependencies (e.g. `php` requires `markup-templating`) & download them
  93. const deps: string[] | string | undefined =
  94. prismComponents.languages[language].require;
  95. const depsArray = Array.isArray(deps) ? deps : [deps];
  96. await Promise.all(
  97. depsArray.map(dep => {
  98. if (!dep) {
  99. return Promise.resolve();
  100. }
  101. return import(`prismjs/components/prism-${dep}.min`);
  102. })
  103. );
  104. // Download language grammar file
  105. await import(`prismjs/components/prism-${language}.min`);
  106. onLoad?.();
  107. } catch (error) {
  108. // eslint-disable-next-line no-console
  109. console.warn(
  110. `Cannot download Prism grammar file for \`${lang}\`. Check the internet connection, and the \`lang\` argument passed to \`loadPrismLanguage()\`.`
  111. );
  112. onError?.(error);
  113. }
  114. }