Просмотр исходного кода

WIP: Imprv: replace marked

testing with markdown-it
Yuki Takei 8 лет назад
Родитель
Сommit
2bc8f18357

+ 17 - 13
lib/crowi/index.js

@@ -71,19 +71,7 @@ Crowi.prototype.init = function() {
   }).then(function() {
     return self.setupSessionConfig();
   }).then(function() {
-    return new Promise(function(resolve, reject) {
-      self.model('Config', require('../models/config')(self));
-      var Config = self.model('Config');
-      Config.loadAllConfig(function(err, doc) {
-        if (err) {
-          return reject();
-        }
-
-        self.setConfig(doc);
-
-        return resolve();
-      });
-    });
+    return self.setupAppConfig();
   }).then(function() {
     return self.scanRuntimeVersions();
   }).then(function() {
@@ -204,6 +192,22 @@ Crowi.prototype.setupSessionConfig = function() {
   });
 };
 
+Crowi.prototype.setupAppConfig = function() {
+  return new Promise((resolve, reject) => {
+    this.model('Config', require('../models/config')(this));
+    var Config = this.model('Config');
+    Config.loadAllConfig((err, doc) => {
+      if (err) {
+        return reject();
+      }
+
+      this.setConfig(doc);
+
+      return resolve();
+    });
+  });
+}
+
 Crowi.prototype.setupModels = function() {
   var self = this
     ;

+ 2 - 2
package.json

@@ -85,14 +85,14 @@
     "get-line-from-pos": "^1.0.0",
     "googleapis": "^25.0.0",
     "graceful-fs": "^4.1.11",
-    "highlight.js": "^9.10.0",
+    "highlight.js": "^9.12.0",
     "i18next": "^10.0.1",
     "i18next-express-middleware": "^1.0.5",
     "i18next-node-fs-backend": "^1.0.0",
     "i18next-sprintf-postprocessor": "^0.2.2",
     "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
-    "marked": "^0.3.12",
+    "markdown-it": "^8.4.0",
     "md5": "^2.2.1",
     "method-override": "^2.3.10",
     "mkdirp": "~0.5.1",

+ 3 - 2
resource/js/app.js

@@ -2,7 +2,8 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 
 import Crowi from './util/Crowi';
-import CrowiRenderer from './util/CrowiRenderer';
+// import CrowiRenderer from './util/CrowiRenderer';
+import GrowiRenderer from './util/GrowiRenderer';
 
 import HeaderSearchBox  from './components/HeaderSearchBox';
 import SearchPage       from './components/SearchPage';
@@ -54,7 +55,7 @@ window.crowi = crowi;
 crowi.setConfig(JSON.parse(document.getElementById('crowi-context-hydrate').textContent || '{}'));
 crowi.fetchUsers();
 
-const crowiRenderer = new CrowiRenderer(crowi);
+const crowiRenderer = new GrowiRenderer(crowi);
 window.crowiRenderer = crowiRenderer;
 
 // FIXME

+ 1 - 3
resource/js/components/Page/PageBody.js

@@ -34,7 +34,7 @@ export default class PageBody extends React.Component {
       body = this.props.page.revision.body;
     }
 
-    body = this.crowiRenderer.render(body, undefined, this.props.rendererOptions);
+    body = this.crowiRenderer.render(body, undefined);
 
     if (this.props.highlightKeywords) {
       body = this.getHighlightBody(body, this.props.highlightKeywords);
@@ -59,12 +59,10 @@ PageBody.propTypes = {
   page: PropTypes.object.isRequired,
   highlightKeywords: PropTypes.string,
   pageBody: PropTypes.string,
-  rendererOptions: PropTypes.object,
 };
 
 PageBody.defaultProps = {
   page: {},
   pageBody: '',
-  rendererOptions: {},
 };
 

+ 1 - 9
resource/js/components/PageEditor.js

@@ -244,14 +244,6 @@ export default class PageEditor extends React.Component {
 
     this.setState({ markdown: value });
 
-    // generate options obj
-    const rendererOptions = {
-      // see: https://www.npmjs.com/package/marked
-      marked: {
-        breaks: config.isEnabledLineBreaks,
-      }
-    };
-
     // render html
     var context = {
       markdown: this.state.markdown,
@@ -266,7 +258,7 @@ export default class PageEditor extends React.Component {
       })
       .then(() => crowi.interceptorManager.process('postPreProcess', context))
       .then(() => {
-        var parsedHTML = crowiRenderer.render(context.markdown, context.dom, rendererOptions);
+        var parsedHTML = crowiRenderer.render(context.markdown, context.dom);
         context['parsedHTML'] = parsedHTML;
       })
       .then(() => crowi.interceptorManager.process('postRenderPreview', context))

+ 0 - 9
resource/js/components/SearchPage/SearchResultList.js

@@ -12,14 +12,6 @@ export default class SearchResultList extends React.Component {
   render() {
     var isEnabledLineBreaks = $('#content-main').data('linebreaks-enabled');
 
-    // generate options obj
-    var rendererOptions = {
-      // see: https://www.npmjs.com/package/marked
-      marked: {
-        breaks: isEnabledLineBreaks
-      }
-    };
-
     const resultList = this.props.pages.map((page) => {
       const pageBody = page.revision.body;
       return (
@@ -31,7 +23,6 @@ export default class SearchResultList extends React.Component {
               page={page}
               pageBody={pageBody}
               highlightKeywords={this.props.searchingKeyword}
-              rendererOptions={rendererOptions}
             />
           </div>
         </div>

+ 1 - 1
resource/js/legacy/crowi-form.js

@@ -86,7 +86,7 @@ $(function() {
       })
       .then(() => crowi.interceptorManager.process('postPreProcess', context))
       .then(() => {
-        var parsedHTML = crowiRenderer.render(context.markdown, context.dom, rendererOptions);
+        var parsedHTML = crowiRenderer.render(context.markdown, context.dom);
         context['parsedHTML'] = parsedHTML;
       })
       .then(() => crowi.interceptorManager.process('postRenderPreview', context))

+ 2 - 10
resource/js/legacy/crowi.js

@@ -214,14 +214,6 @@ $(function() {
   var pagePath= $('#content-main').data('path');
   var isSavedStatesOfTabChanges = config['isSavedStatesOfTabChanges'];
 
-  // generate options obj
-  var rendererOptions = {
-    // see: https://www.npmjs.com/package/marked
-    marked: {
-      breaks: config.isEnabledLineBreaks
-    }
-  };
-
   $('[data-toggle="popover"]').popover();
   $('[data-toggle="tooltip"]').tooltip();
   $('[data-tooltip-stay]').tooltip('show');
@@ -488,7 +480,7 @@ $(function() {
         var revisionPath = '#' + id + ' .revision-path';
 
         var markdown = entities.decodeHTML($(contentId).html());
-        var parsedHTML = crowiRenderer.render(markdown, $revisionBody.get(0), rendererOptions);
+        var parsedHTML = crowiRenderer.render(markdown, $revisionBody.get(0));
         $revisionBody.html(parsedHTML);
 
         $('.template-create-button', revisionBody).on('click', function() {
@@ -551,7 +543,7 @@ $(function() {
         .then(() => crowi.interceptorManager.process('postPreProcess', context))
         .then(() => {
           var revisionBody = $('#revision-body-content');
-          var parsedHTML = crowiRenderer.render(context.markdown, context.dom, rendererOptions);
+          var parsedHTML = crowiRenderer.render(context.markdown, context.dom);
           context.parsedHTML = parsedHTML;
           Promise.resolve(context);
         })

+ 12 - 3
resource/js/util/CrowiRenderer.js

@@ -1,3 +1,7 @@
+/**
+ * DEPRECATED
+ * replaced by GrowiRenderer -- 2018.01.29 Yuki Takei
+ *
 import marked from 'marked';
 import hljs from 'highlight.js';
 import * as entities from 'entities';
@@ -16,7 +20,6 @@ import PlantUML from './LangProcessor/PlantUML';
 
 export default class CrowiRenderer {
 
-
   constructor(crowi) {
     this.crowi = crowi;
 
@@ -134,6 +137,7 @@ export default class CrowiRenderer {
 
     return parsed;
   }
+  */
 
   /**
    * render
@@ -150,12 +154,17 @@ export default class CrowiRenderer {
    *
    * @memberOf CrowiRenderer
    */
-  render(markdown, dom, rendererOptions) {
+  /*
+   DEPRECATED
+   replaced by GrowiRenderer -- 2018.01.29 Yuki Takei
+
+  render(markdown, dom) {
     let html = '';
 
-    html = this.parseMarkdown(markdown, dom, rendererOptions.marked || {});
+    html = this.parseMarkdown(markdown, dom);
     html = this.postProcess(html, dom);
 
     return html;
   }
+  */
 }

+ 173 - 0
resource/js/util/GrowiRenderer.js

@@ -0,0 +1,173 @@
+import MarkdownIt from 'markdown-it';
+// import hljs from 'highlight.js';
+import * as entities from 'entities';
+
+import MarkdownFixer from './PreProcessor/MarkdownFixer';
+import Linker        from './PreProcessor/Linker';
+import ImageExpander from './PreProcessor/ImageExpander';
+import XssFilter     from './PreProcessor/XssFilter';
+
+import Emoji         from './PostProcessor/Emoji';
+import Mathjax       from './PostProcessor/Mathjax';
+
+import Tsv2Table from './LangProcessor/Tsv2Table';
+import Template from './LangProcessor/Template';
+import PlantUML from './LangProcessor/PlantUML';
+
+export default class GrowiRenderer {
+
+
+  constructor(crowi) {
+    this.crowi = crowi;
+
+    this.md = new MarkdownIt();
+    this.configure(this.crowi.getConfig());
+
+    this.preProcessors = [
+      new Linker(crowi),
+      new ImageExpander(crowi),
+      new XssFilter(crowi),
+    ];
+    this.postProcessors = [
+      new Emoji(crowi),
+      new Mathjax(crowi),
+    ];
+
+    this.langProcessors = {
+      'tsv': new Tsv2Table(crowi),
+      'tsv-h': new Tsv2Table(crowi, {header: true}),
+      'template': new Template(crowi),
+      'plantuml': new PlantUML(crowi),
+    };
+
+    this.configure = this.configure.bind(this);
+    this.parseMarkdown = this.parseMarkdown.bind(this);
+    this.codeRenderer = this.codeRenderer.bind(this);
+  }
+
+  /**
+   * configure markdown-it
+   * @param {any} config
+   */
+  configure(config) {
+    this.md.set({
+      html: true,
+      linkify: true,
+      breaks: config.isEnabledLineBreaks,
+    });
+  }
+
+  preProcess(markdown, dom) {
+    for (let i = 0; i < this.preProcessors.length; i++) {
+      if (!this.preProcessors[i].process) {
+        continue;
+      }
+      markdown = this.preProcessors[i].process(markdown, dom);
+    }
+    return markdown;
+  }
+
+  postProcess(html, dom) {
+    for (let i = 0; i < this.postProcessors.length; i++) {
+      if (!this.postProcessors[i].process) {
+        continue;
+      }
+      html = this.postProcessors[i].process(html, dom);
+    }
+
+    return html;
+  }
+
+  codeRenderer(code, lang, escaped) {
+    let result = '', hl;
+
+    // if (lang) {
+    //   const langAndFn = lang.split(':');
+    //   const langPattern = langAndFn[0];
+    //   const langFn = langAndFn[1] || null;
+    //   if (this.langProcessors[langPattern]) {
+    //     return this.langProcessors[langPattern].process(code, lang);
+    //   }
+
+    //   try {
+    //     hl = hljs.highlight(langPattern, code);
+    //     result = hl.value;
+    //     escaped = true;
+    //   } catch (e) {
+    //     result = code;
+    //   }
+
+    //   result = (escape ? result : entities.encodeHTML(result));
+
+    //   let citeTag = '';
+    //   if (langFn) {
+    //     citeTag = `<cite>${langFn}</cite>`;
+    //   }
+    //   return `<pre class="wiki-code wiki-lang">${citeTag}<code class="lang-${lang}">${result}\n</code></pre>\n`;
+    // }
+
+    // no lang specified
+    return `<pre class="wiki-code"><code>${entities.encodeHTML(code)}\n</code></pre>`;
+
+  }
+
+  parseMarkdown(markdown, dom, markedOpts) {
+    let parsed = '';
+
+    /*
+    const markedRenderer = new marked.Renderer();
+    markedRenderer.code = this.codeRenderer;
+
+    try {
+      // concat
+      let concatMarkedOpts = Object.assign({
+        gfm: true,
+        tables: true,
+        breaks: true,
+        pedantic: false,
+        sanitize: false,
+        smartLists: true,
+        smartypants: false,
+        renderer: markedRenderer,
+      }, markedOpts);
+
+      marked.setOptions(concatMarkedOpts);
+
+      // override
+      marked.Lexer.lex = function(src, options) {
+        var lexer = new marked.Lexer(options);
+
+        // this is maybe not an official way
+        if (lexer.rules) {
+          lexer.rules.fences = /^ *(`{3,}|~{3,})[ \.]*([^\r\n]+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/;
+        }
+
+        return lexer.lex(src);
+      };
+
+      parsed = marked(markdown);
+    } catch (e) { console.log(e, e.stack); }
+    */
+
+    parsed = this.md.render(markdown);
+    return parsed;
+  }
+
+  /**
+   * render
+   *
+   * @param {string} markdown
+   * @param {Element} dom
+   * @returns
+   *
+   * @memberOf CrowiRenderer
+   */
+  render(markdown, dom) {
+    let html = '';
+
+    html = this.parseMarkdown(markdown, dom);
+    html = this.postProcess(html, dom);
+
+    return html;
+  }
+}

+ 27 - 3
yarn.lock

@@ -2069,7 +2069,7 @@ enhanced-resolve@^3.4.0:
     object-assign "^4.0.1"
     tapable "^0.2.7"
 
-entities@^1.1.1:
+entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
@@ -2811,7 +2811,7 @@ he@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
 
-highlight.js@^9.10.0:
+highlight.js@^9.12.0:
   version "9.12.0"
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
 
@@ -3358,6 +3358,12 @@ ldapjs@^1.0.1:
   optionalDependencies:
     dtrace-provider "^0.7.0"
 
+linkify-it@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
+  dependencies:
+    uc.micro "^1.0.1"
+
 livereload-js@^2.2.2:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.2.2.tgz#6c87257e648ab475bc24ea257457edcc1f8d0bc2"
@@ -3661,6 +3667,16 @@ map-values@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/map-values/-/map-values-1.0.1.tgz#768b8e79c009bf2b64fee806e22a7b1c4190c990"
 
+markdown-it@^8.4.0:
+  version "8.4.0"
+  resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d"
+  dependencies:
+    argparse "^1.0.7"
+    entities "~1.1.1"
+    linkify-it "^2.0.0"
+    mdurl "^1.0.1"
+    uc.micro "^1.0.3"
+
 marked-terminal@^1.6.2:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-1.7.0.tgz#c8c460881c772c7604b64367007ee5f77f125904"
@@ -3671,7 +3687,7 @@ marked-terminal@^1.6.2:
     lodash.assign "^4.2.0"
     node-emoji "^1.4.1"
 
-marked@^0.3.12, marked@^0.3.6:
+marked@^0.3.6:
   version "0.3.12"
   resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
 
@@ -3694,6 +3710,10 @@ md5@^2.2.1:
     crypt "~0.0.1"
     is-buffer "~1.1.1"
 
+mdurl@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -6046,6 +6066,10 @@ ua-parser-js@^0.7.9:
   version "0.7.17"
   resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
 
+uc.micro@^1.0.1, uc.micro@^1.0.3:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
+
 uglify-js@2.6.0:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.6.0.tgz#25eaa1cc3550e39410ceefafd1cfbb6b6d15f001"