Yuki Takei 3 лет назад
Родитель
Сommit
89ed4bc6dc
1 измененных файлов с 248 добавлено и 190 удалено
  1. 248 190
      packages/app/src/services/renderer/growi-renderer.ts

+ 248 - 190
packages/app/src/services/renderer/growi-renderer.ts

@@ -1,29 +1,40 @@
-import { isClient } from '@growi/core';
-import MarkdownIt from 'markdown-it';
+import React from 'react';
+
+import rehype2react from 'rehype-react';
+import slug from 'rehype-slug';
+import toc, { HtmlElementNode } from 'rehype-toc';
+import breaks from 'remark-breaks';
+import emoji from 'remark-emoji';
+import footnotes from 'remark-footnotes';
+import gfm from 'remark-gfm';
+import parse from 'remark-parse';
+import remark2rehype from 'remark-rehype';
+import {
+  unified, Plugin, PluginTuple, Processor,
+} from 'unified';
 
 import { Nullable } from '~/interfaces/common'; // TODO: Remove this asap when the ContextExtractor is removed
-import { CustomWindow } from '~/interfaces/global';
 import { GrowiRendererConfig, RendererSettings } from '~/interfaces/services/renderer';
 import loggerFactory from '~/utils/logger';
 
-import CsvToTable from './PreProcessor/CsvToTable';
-import EasyGrid from './PreProcessor/EasyGrid';
-import Linker from './PreProcessor/Linker';
-import XssFilter from './PreProcessor/XssFilter';
-import BlockdiagConfigurer from './markdown-it/blockdiag';
-import DrawioViewerConfigurer from './markdown-it/drawio-viewer';
-import EmojiConfigurer from './markdown-it/emoji';
-import FooternoteConfigurer from './markdown-it/footernote';
-import HeaderConfigurer from './markdown-it/header';
-import HeaderLineNumberConfigurer from './markdown-it/header-line-number';
-import HeaderWithEditLinkConfigurer from './markdown-it/header-with-edit-link';
-import LinkerByRelativePathConfigurer from './markdown-it/link-by-relative-path';
-import MathJaxConfigurer from './markdown-it/mathjax';
-import PlantUMLConfigurer from './markdown-it/plantuml';
-import TableConfigurer from './markdown-it/table';
-import TableWithHandsontableButtonConfigurer from './markdown-it/table-with-handsontable-button';
-import TaskListsConfigurer from './markdown-it/task-lists';
-import TocAndAnchorConfigurer from './markdown-it/toc-and-anchor';
+// import CsvToTable from './PreProcessor/CsvToTable';
+// import EasyGrid from './PreProcessor/EasyGrid';
+// import Linker from './PreProcessor/Linker';
+// import XssFilter from './PreProcessor/XssFilter';
+// import BlockdiagConfigurer from './markdown-it/blockdiag';
+// import DrawioViewerConfigurer from './markdown-it/drawio-viewer';
+// import EmojiConfigurer from './markdown-it/emoji';
+// import FooternoteConfigurer from './markdown-it/footernote';
+// import HeaderConfigurer from './markdown-it/header';
+// import HeaderLineNumberConfigurer from './markdown-it/header-line-number';
+// import HeaderWithEditLinkConfigurer from './markdown-it/header-with-edit-link';
+// import LinkerByRelativePathConfigurer from './markdown-it/link-by-relative-path';
+// import MathJaxConfigurer from './markdown-it/mathjax';
+// import PlantUMLConfigurer from './markdown-it/plantuml';
+// import TableConfigurer from './markdown-it/table';
+// import TableWithHandsontableButtonConfigurer from './markdown-it/table-with-handsontable-button';
+// import TaskListsConfigurer from './markdown-it/task-lists';
+// import TocAndAnchorConfigurer from './markdown-it/toc-and-anchor';
 
 
 const logger = loggerFactory('growi:util:GrowiRenderer');
@@ -34,166 +45,199 @@ type MarkdownSettings = {
   breaks?: boolean,
 };
 
-export default class GrowiRenderer {
+function applyPlugin(processor: Processor, plugin: Plugin | PluginTuple): Processor {
+  if (Array.isArray(plugin)) {
+    return processor.use(...plugin);
+  }
 
-  preProcessors: any[];
+  return processor.use(plugin);
+}
 
-  postProcessors: any[];
+export default class GrowiRenderer {
 
-  md: any;
+  // preProcessors: any[];
 
-  isMarkdownItConfigured: boolean;
+  // postProcessors: any[];
 
-  markdownItConfigurers: any[];
+  // md: any;
 
-  growiRendererConfig: GrowiRendererConfig;
+  // isMarkdownItConfigured: boolean;
 
-  pagePath?: Nullable<string>;
+  // markdownItConfigurers: any[];
 
-  /**
-   *
-   * @param {string} mode
-   */
-  constructor(growiRendererConfig: GrowiRendererConfig, pagePath?: Nullable<string>) {
-    this.growiRendererConfig = growiRendererConfig;
-    this.pagePath = pagePath;
-
-    if (isClient() && (window as CustomWindow).growiRenderer != null) {
-      this.preProcessors = (window as CustomWindow).growiRenderer.preProcessors;
-      this.postProcessors = (window as CustomWindow).growiRenderer.postProcessors;
-    }
-    else {
-      this.preProcessors = [
-        new EasyGrid(),
-        new Linker(),
-        new CsvToTable(),
-        new XssFilter({
-          isEnabledXssPrevention: this.growiRendererConfig.isEnabledXssPrevention,
-          tagWhiteList: this.growiRendererConfig.tagWhiteList,
-          attrWhiteList: this.growiRendererConfig.attrWhiteList,
-        }),
-      ];
-      this.postProcessors = [
-      ];
-    }
-
-    this.init = this.init.bind(this);
-    this.addConfigurers = this.addConfigurers.bind(this);
-    this.setMarkdownSettings = this.setMarkdownSettings.bind(this);
-    this.configure = this.configure.bind(this);
-    this.process = this.process.bind(this);
-    this.codeRenderer = this.codeRenderer.bind(this);
-  }
-
-  init() {
-    // init markdown-it
-    this.md = new MarkdownIt({
-      html: true,
-      linkify: true,
-      highlight: this.codeRenderer,
-    });
-
-    this.isMarkdownItConfigured = false;
-
-    this.markdownItConfigurers = [
-      new TaskListsConfigurer(),
-      new HeaderConfigurer(),
-      new EmojiConfigurer(),
-      new MathJaxConfigurer(),
-      new DrawioViewerConfigurer(),
-      new PlantUMLConfigurer(this.growiRendererConfig),
-      new BlockdiagConfigurer(this.growiRendererConfig),
-    ];
-
-    if (this.pagePath != null) {
-      this.markdownItConfigurers.push(
-        new LinkerByRelativePathConfigurer(this.pagePath),
-      );
-    }
-  }
+  // pagePath?: Nullable<string>;
 
-  addConfigurers(configurers: any[]): void {
-    this.markdownItConfigurers.push(...configurers);
-  }
+  remarkPlugins: (Plugin | PluginTuple)[] = [
+    gfm,
+  ];
 
-  setMarkdownSettings(settings: MarkdownSettings): void {
-    this.md.set(settings);
-  }
+  rehypePlugins: (Plugin | PluginTuple)[] = [
+    slug,
+  ];
 
-  configure(): void {
-    if (!this.isMarkdownItConfigured) {
-      this.markdownItConfigurers.forEach((configurer) => {
-        configurer.configure(this.md);
-      });
-    }
-  }
+  processor?: Processor;
 
-  preProcess(markdown, context) {
-    let processed = markdown;
-    for (let i = 0; i < this.preProcessors.length; i++) {
-      if (!this.preProcessors[i].process) {
-        continue;
-      }
-      processed = this.preProcessors[i].process(processed, context);
-    }
+  growiRendererConfig: GrowiRendererConfig;
 
-    return processed;
+  constructor(growiRendererConfig: GrowiRendererConfig, pagePath?: Nullable<string>) {
+    this.growiRendererConfig = growiRendererConfig;
+    // this.pagePath = pagePath;
+
+    // if (isClient() && (window as CustomWindow).growiRenderer != null) {
+    //   this.preProcessors = (window as CustomWindow).growiRenderer.preProcessors;
+    //   this.postProcessors = (window as CustomWindow).growiRenderer.postProcessors;
+    // }
+    // else {
+    //   this.preProcessors = [
+    //     new EasyGrid(),
+    //     new Linker(),
+    //     new CsvToTable(),
+    //     new XssFilter({
+    //       isEnabledXssPrevention: this.growiRendererConfig.isEnabledXssPrevention,
+    //       tagWhiteList: this.growiRendererConfig.tagWhiteList,
+    //       attrWhiteList: this.growiRendererConfig.attrWhiteList,
+    //     }),
+    //   ];
+    //   this.postProcessors = [
+    //   ];
+    // }
+
+    // this.init = this.init.bind(this);
+    // this.addConfigurers = this.addConfigurers.bind(this);
+    // this.setMarkdownSettings = this.setMarkdownSettings.bind(this);
+    // this.configure = this.configure.bind(this);
+    // this.process = this.process.bind(this);
+    // this.codeRenderer = this.codeRenderer.bind(this);
   }
 
-  process(markdown, context) {
-    return this.md.render(markdown, context);
-  }
+  init() {
+    let parser: Processor = unified().use(parse);
+    this.remarkPlugins.forEach((item) => {
+      parser = applyPlugin(parser, item);
+    });
 
-  postProcess(html, context) {
-    let processed = html;
-    for (let i = 0; i < this.postProcessors.length; i++) {
-      if (!this.postProcessors[i].process) {
-        continue;
-      }
-      processed = this.postProcessors[i].process(processed, context);
-    }
+    let rehype: Processor = parser.use(remark2rehype);
+    this.rehypePlugins.forEach((item) => {
+      rehype = applyPlugin(rehype, item);
+    });
 
-    return processed;
+    this.processor = rehype.use(rehype2react, {
+      createElement: React.createElement,
+      components: {
+        // a: NextLink,
+      },
+    });
   }
 
-  codeRenderer(code, langExt) {
-    const noborder = (!this.growiRendererConfig.highlightJsStyleBorder) ? 'hljs-no-border' : '';
-
-    let citeTag = '';
-    let hljsLang = 'plaintext';
-    let showLinenumbers = false;
-
-    if (langExt) {
-      // https://regex101.com/r/qGs7eZ/3
-      const match = langExt.match(/^([^:=\n]+)?(=([^:=\n]*))?(:([^:=\n]*))?(=([^:=\n]*))?$/);
-
-      const lang = match[1];
-      const fileName = match[5] || null;
-      showLinenumbers = (match[2] != null) || (match[6] != null);
-
-      if (fileName != null) {
-        citeTag = `<cite>${fileName}</cite>`;
-      }
-      if (hljs.getLanguage(lang)) {
-        hljsLang = lang;
-      }
-    }
-
-    let highlightCode = code;
-    try {
-      highlightCode = hljs.highlight(hljsLang, code, true).value;
-
-      // add line numbers
-      if (showLinenumbers) {
-        highlightCode = hljs.lineNumbersValue((highlightCode));
-      }
-    }
-    catch (err) {
-      logger.error(err);
-    }
-
-    return `<pre class="hljs ${noborder}">${citeTag}<code>${highlightCode}</code></pre>`;
-  }
+  // init() {
+  //   // init markdown-it
+  //   this.md = new MarkdownIt({
+  //     html: true,
+  //     linkify: true,
+  //     highlight: this.codeRenderer,
+  //   });
+
+  //   this.isMarkdownItConfigured = false;
+
+  //   this.markdownItConfigurers = [
+  //     new TaskListsConfigurer(),
+  //     new HeaderConfigurer(),
+  //     new EmojiConfigurer(),
+  //     new MathJaxConfigurer(),
+  //     new DrawioViewerConfigurer(),
+  //     new PlantUMLConfigurer(this.growiRendererConfig),
+  //     new BlockdiagConfigurer(this.growiRendererConfig),
+  //   ];
+
+  //   if (this.pagePath != null) {
+  //     this.markdownItConfigurers.push(
+  //       new LinkerByRelativePathConfigurer(this.pagePath),
+  //     );
+  //   }
+  // }
+
+  // addConfigurers(configurers: any[]): void {
+  //   this.markdownItConfigurers.push(...configurers);
+  // }
+
+  // setMarkdownSettings(settings: MarkdownSettings): void {
+  //   this.md.set(settings);
+  // }
+
+  // configure(): void {
+  //   if (!this.isMarkdownItConfigured) {
+  //     this.markdownItConfigurers.forEach((configurer) => {
+  //       configurer.configure(this.md);
+  //     });
+  //   }
+  // }
+
+  // preProcess(markdown, context) {
+  //   let processed = markdown;
+  //   for (let i = 0; i < this.preProcessors.length; i++) {
+  //     if (!this.preProcessors[i].process) {
+  //       continue;
+  //     }
+  //     processed = this.preProcessors[i].process(processed, context);
+  //   }
+
+  //   return processed;
+  // }
+
+  // process(markdown, context) {
+  //   return this.md.render(markdown, context);
+  // }
+
+  // postProcess(html, context) {
+  //   let processed = html;
+  //   for (let i = 0; i < this.postProcessors.length; i++) {
+  //     if (!this.postProcessors[i].process) {
+  //       continue;
+  //     }
+  //     processed = this.postProcessors[i].process(processed, context);
+  //   }
+
+  //   return processed;
+  // }
+
+  // codeRenderer(code, langExt) {
+  //   const noborder = (!this.growiRendererConfig.highlightJsStyleBorder) ? 'hljs-no-border' : '';
+
+  //   let citeTag = '';
+  //   let hljsLang = 'plaintext';
+  //   let showLinenumbers = false;
+
+  //   if (langExt) {
+  //     // https://regex101.com/r/qGs7eZ/3
+  //     const match = langExt.match(/^([^:=\n]+)?(=([^:=\n]*))?(:([^:=\n]*))?(=([^:=\n]*))?$/);
+
+  //     const lang = match[1];
+  //     const fileName = match[5] || null;
+  //     showLinenumbers = (match[2] != null) || (match[6] != null);
+
+  //     if (fileName != null) {
+  //       citeTag = `<cite>${fileName}</cite>`;
+  //     }
+  //     if (hljs.getLanguage(lang)) {
+  //       hljsLang = lang;
+  //     }
+  //   }
+
+  //   let highlightCode = code;
+  //   try {
+  //     highlightCode = hljs.highlight(hljsLang, code, true).value;
+
+  //     // add line numbers
+  //     if (showLinenumbers) {
+  //       highlightCode = hljs.lineNumbersValue((highlightCode));
+  //     }
+  //   }
+  //   catch (err) {
+  //     logger.error(err);
+  //   }
+
+  //   return `<pre class="hljs ${noborder}">${citeTag}<code>${highlightCode}</code></pre>`;
+  // }
 
 }
 
@@ -205,19 +249,33 @@ export const generateViewRenderer: RendererGenerator = (
     growiRendererConfig: GrowiRendererConfig, rendererSettings: RendererSettings, pagePath?: Nullable<string>,
 ): GrowiRenderer => {
   const renderer = new GrowiRenderer(growiRendererConfig, pagePath);
+  // add remark plugins
+  renderer.remarkPlugins.push(footnotes);
+  renderer.remarkPlugins.push(emoji);
+  if (rendererSettings.isEnabledLinebreaks) {
+    renderer.remarkPlugins.push(breaks);
+  }
+  // add rehypePlugins
+  // renderer.rehypePlugins.push([toc, {
+  //   headings: ['h1', 'h2', 'h3'],
+  //   customizeTOC: storeTocNode,
+  // }]);
+  // renderer.rehypePlugins.push([autoLinkHeadings, {
+  //   behavior: 'append',
+  // }]);
   renderer.init();
 
-  // Add configurers for viewer
-  renderer.addConfigurers([
-    new FooternoteConfigurer(),
-    new TocAndAnchorConfigurer(),
-    new HeaderLineNumberConfigurer(),
-    new HeaderWithEditLinkConfigurer(),
-    new TableWithHandsontableButtonConfigurer(),
-  ]);
+  // // Add configurers for viewer
+  // renderer.addConfigurers([
+  //   new FooternoteConfigurer(),
+  //   new TocAndAnchorConfigurer(),
+  //   new HeaderLineNumberConfigurer(),
+  //   new HeaderWithEditLinkConfigurer(),
+  //   new TableWithHandsontableButtonConfigurer(),
+  // ]);
 
-  renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
-  renderer.configure();
+  // renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
+  // renderer.configure();
 
   return renderer;
 };
@@ -228,15 +286,15 @@ export const generatePreviewRenderer: RendererGenerator = (
   const renderer = new GrowiRenderer(growiRendererConfig, pagePath);
   renderer.init();
 
-  // Add configurers for preview
-  renderer.addConfigurers([
-    new FooternoteConfigurer(),
-    new HeaderLineNumberConfigurer(),
-    new TableConfigurer(),
-  ]);
+  // // Add configurers for preview
+  // renderer.addConfigurers([
+  //   new FooternoteConfigurer(),
+  //   new HeaderLineNumberConfigurer(),
+  //   new TableConfigurer(),
+  // ]);
 
-  renderer.setMarkdownSettings({ breaks: rendererSettings?.isEnabledLinebreaks });
-  renderer.configure();
+  // renderer.setMarkdownSettings({ breaks: rendererSettings?.isEnabledLinebreaks });
+  // renderer.configure();
 
   return renderer;
 };
@@ -247,12 +305,12 @@ export const generateCommentPreviewRenderer: RendererGenerator = (
   const renderer = new GrowiRenderer(growiRendererConfig, pagePath);
   renderer.init();
 
-  renderer.addConfigurers([
-    new TableConfigurer(),
-  ]);
+  // renderer.addConfigurers([
+  //   new TableConfigurer(),
+  // ]);
 
-  renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaksInComments });
-  renderer.configure();
+  // renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaksInComments });
+  // renderer.configure();
 
   return renderer;
 };
@@ -263,12 +321,12 @@ export const generateOthersRenderer: RendererGenerator = (
   const renderer = new GrowiRenderer(growiRendererConfig, pagePath);
   renderer.init();
 
-  renderer.addConfigurers([
-    new TableConfigurer(),
-  ]);
+  // renderer.addConfigurers([
+  //   new TableConfigurer(),
+  // ]);
 
-  renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
-  renderer.configure();
+  // renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
+  // renderer.configure();
 
   return renderer;
 };