slides.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import type { Schema as SanitizeOption } from 'hast-util-sanitize';
  2. import type { Root } from 'mdast';
  3. import { frontmatterToMarkdown } from 'mdast-util-frontmatter';
  4. import { gfmToMarkdown } from 'mdast-util-gfm';
  5. import { toMarkdown } from 'mdast-util-to-markdown';
  6. import type { Plugin } from 'unified';
  7. import type { Node } from 'unist';
  8. import { visit } from 'unist-util-visit';
  9. import { parseSlideFrontmatter } from '../parse-slide-frontmatter';
  10. const SUPPORTED_ATTRIBUTES = ['children', 'marp'];
  11. const nodeToMakrdown = (node: Node) => {
  12. return toMarkdown(node as Root, {
  13. extensions: [
  14. frontmatterToMarkdown(['yaml']),
  15. gfmToMarkdown(),
  16. ],
  17. });
  18. };
  19. // Allow node tree to be converted to markdown
  20. const removeCustomType = (tree: Node) => {
  21. // Try toMarkdown() on all Node.
  22. visit(tree, (node) => {
  23. const tmp = node?.children;
  24. node.children = [];
  25. try {
  26. nodeToMakrdown(node);
  27. }
  28. catch (err) {
  29. // if some Node cannot convert to markdown, change to a convertible type
  30. node.type = 'text';
  31. node.value = '';
  32. }
  33. finally {
  34. node.children = tmp;
  35. }
  36. });
  37. };
  38. const rewriteNode = (tree: Node, node: Node, isEnabledMarp: boolean) => {
  39. const [marp, slide] = parseSlideFrontmatter(node.value as string);
  40. if ((marp && isEnabledMarp) || slide) {
  41. removeCustomType(tree);
  42. const markdown = nodeToMakrdown(tree);
  43. const newNode: Node = {
  44. type: 'root',
  45. data: {},
  46. position: tree.position,
  47. children: tree.children,
  48. };
  49. const data = newNode.data ?? (newNode.data = {});
  50. tree.children = [newNode];
  51. data.hName = 'slide';
  52. data.hProperties = {
  53. marp: (marp && isEnabledMarp) ? '' : undefined,
  54. children: markdown,
  55. };
  56. }
  57. };
  58. type SlidePluginParams = {
  59. isEnabledMarp: boolean,
  60. }
  61. export const remarkPlugin: Plugin<[SlidePluginParams]> = (options) => {
  62. return (tree) => {
  63. visit(tree, (node) => {
  64. if (node.type === 'yaml' && node.value != null) {
  65. rewriteNode(tree, node, options.isEnabledMarp);
  66. }
  67. });
  68. };
  69. };
  70. export const sanitizeOption: SanitizeOption = {
  71. tagNames: ['slide'],
  72. attributes: {
  73. slide: SUPPORTED_ATTRIBUTES,
  74. },
  75. };