convert-markdown-to-html.ts 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import { dynamicImport } from '@cspell/dynamic-import';
  2. import type { Root, Code } from 'mdast';
  3. import type * as RehypeMeta from 'rehype-meta';
  4. import type * as RehypeStringify from 'rehype-stringify';
  5. import type * as RemarkParse from 'remark-parse';
  6. import type * as RemarkRehype from 'remark-rehype';
  7. import type * as Unified from 'unified';
  8. import type * as UnistUtilVisit from 'unist-util-visit';
  9. interface ModuleCache {
  10. unified?: typeof Unified.unified;
  11. visit?: typeof UnistUtilVisit.visit;
  12. remarkParse?: typeof RemarkParse.default;
  13. remarkRehype?: typeof RemarkRehype.default;
  14. rehypeMeta?: typeof RehypeMeta.default;
  15. rehypeStringify?: typeof RehypeStringify.default;
  16. }
  17. let moduleCache: ModuleCache = {};
  18. const initializeModules = async(): Promise<void> => {
  19. if (moduleCache.unified != null
  20. && moduleCache.visit != null
  21. && moduleCache.remarkParse != null
  22. && moduleCache.remarkRehype != null
  23. && moduleCache.rehypeMeta != null
  24. && moduleCache.rehypeStringify != null
  25. ) {
  26. return;
  27. }
  28. const [
  29. { unified },
  30. { visit },
  31. { default: remarkParse },
  32. { default: remarkRehype },
  33. { default: rehypeMeta },
  34. { default: rehypeStringify },
  35. ] = await Promise.all([
  36. dynamicImport<typeof Unified>('unified', __dirname),
  37. dynamicImport<typeof UnistUtilVisit>('unist-util-visit', __dirname),
  38. dynamicImport<typeof RemarkParse>('remark-parse', __dirname),
  39. dynamicImport<typeof RemarkRehype>('remark-rehype', __dirname),
  40. dynamicImport<typeof RehypeMeta>('rehype-meta', __dirname),
  41. dynamicImport<typeof RehypeStringify>('rehype-stringify', __dirname),
  42. ]);
  43. moduleCache = {
  44. unified,
  45. visit,
  46. remarkParse,
  47. remarkRehype,
  48. rehypeMeta,
  49. rehypeStringify,
  50. };
  51. };
  52. export const convertMarkdownToHtml = async({ pagePath, revisionBody }: { pagePath: string, revisionBody: string }): Promise<string> => {
  53. await initializeModules();
  54. const {
  55. unified, visit, remarkParse, remarkRehype, rehypeMeta, rehypeStringify,
  56. } = moduleCache;
  57. if (unified == null || visit == null || remarkParse == null || remarkRehype == null || rehypeMeta == null || rehypeStringify == null) {
  58. throw new Error('Failed to initialize required modules');
  59. }
  60. const sanitizeMarkdown = () => {
  61. return (tree: Root) => {
  62. visit(tree, 'code', (node: Code) => {
  63. if (node.lang === 'drawio') {
  64. node.value = '<!-- drawio content replaced -->';
  65. }
  66. });
  67. };
  68. };
  69. const processor = unified()
  70. .use(remarkParse)
  71. .use(sanitizeMarkdown)
  72. .use(remarkRehype)
  73. .use(rehypeMeta, {
  74. title: pagePath,
  75. })
  76. .use(rehypeStringify);
  77. return processor.processSync(revisionBody).toString();
  78. };