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

Merge branch 'master' into rc/1.0.x

# Conflicts:
#	README.md
Yuki Takei 9 лет назад
Родитель
Сommit
5c3b47838a

+ 1 - 1
README.md

@@ -1,7 +1,7 @@
 ![Crowi](http://res.cloudinary.com/hrscywv4p/image/upload/c_limit,f_auto,h_900,q_80,w_1200/v1/199673/https_www_filepicker_io_api_file_VpYEP32ZQyCZ85u6XCXo_zskpra.png)
 
 <p align="center">
-  <a href="https://heroku.com/deploy?template=https://github.com/weseek/crowi-plus/tree/v1.0.3"><img src="https://www.herokucdn.com/deploy/button.png"></a>
+  <a href="https://heroku.com/deploy"><img src="https://www.herokucdn.com/deploy/button.png"></a>
 </p>
 
 

+ 21 - 3
bin/generate-plugin-definitions-source.js

@@ -4,7 +4,7 @@
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
 const fs = require('graceful-fs');
-const slash = require('slash');
+const normalize = require('normalize-path');
 const swig = require('swig');
 const helpers = require('../config/helpers');
 
@@ -14,15 +14,33 @@ const OUT = helpers.root('tmp/plugins/plugin-definitions.js');
 const PluginUtils = require('../lib/plugins/plugin-utils');
 const pluginUtils = new PluginUtils();
 
+
+// list plugin names
+let pluginNames = pluginUtils.listPluginNames(helpers.root());
+// add from PLUGIN_NAMES_TOBE_LOADED when development
+if (process.env.NODE_ENV === 'development'
+    && process.env.PLUGIN_NAMES_TOBE_LOADED !== undefined
+    && process.env.PLUGIN_NAMES_TOBE_LOADED.length > 0) {
+
+  pluginNamesDev = process.env.PLUGIN_NAMES_TOBE_LOADED.split(',');
+
+  // merge and remove duplicates
+  if (pluginNamesDev.length > 0) {
+    pluginNames = pluginNames.concat(pluginNamesDev);
+    pluginNames = Array.from(new Set(pluginNames));
+  }
+}
+
+
 // get definitions
-const definitions = pluginUtils.listPluginNames(helpers.root())
+const definitions = pluginNames
   .map((name) => {
     return pluginUtils.generatePluginDefinition(name, true);
   })
   .map((definition) => {
     // convert backslash to slash
     definition.entries = definition.entries.map((entryPath) => {
-      return slash(entryPath);
+      return normalize(entryPath);
     });
     return definition;
   });

+ 7 - 0
config/env.dev.js

@@ -0,0 +1,7 @@
+module.exports = {
+  NODE_ENV: 'development',
+  PLUGIN_NAMES_TOBE_LOADED: [
+    // 'crowi-plugin-lsx',
+    // 'crowi-plugin-pukiwiki-like-linker',
+  ]
+}

+ 2 - 1
config/webpack.dev.js

@@ -2,6 +2,7 @@
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
 
+const path = require('path');
 const webpack = require('webpack');
 const helpers = require('./helpers');
 const webpackMerge = require('webpack-merge');
@@ -44,7 +45,7 @@ module.exports = function (options) {
     },
     resolve: {
       extensions: ['.js', '.json'],
-      modules: [helpers.root('src'), helpers.root('node_modules')],
+      modules: [helpers.root('src'), helpers.root('node_modules'), path.join(process.env.HOME, '.node_modules')],
     },
     module: {
       rules: [

+ 30 - 10
lib/crowi/dev.js

@@ -3,34 +3,40 @@ const path = require('path');
 const webpack = require('webpack');
 const helpers = require('./helpers')
 
-
 class CrowiDev {
 
   /**
    * Creates an instance of CrowiDev.
    * @param {Crowi} crowi
-   * @param {any} server http server
-   * @param {any} app express
    *
    * @memberOf CrowiDev
    */
-  constructor(crowi, server, app) {
+  constructor(crowi) {
     this.crowi = crowi;
-    this.server = server;
-    this.app = app;
   }
 
-  setupTools() {
-    this.setupEasyLiveReload();
+  init() {
   }
 
-  setupEasyLiveReload() {
+  /**
+   *
+   *
+   * @param {any} server http server
+   * @param {any} app express
+   *
+   * @memberOf CrowiDev
+   */
+  setup(server, app) {
+    this.setupEasyLiveReload(app);
+  }
+
+  setupEasyLiveReload(app) {
     if (!helpers.hasProcessFlag('watch')) {
       return;
     }
 
     const livereload = require('easy-livereload');
-    this.app.use(livereload({
+    app.use(livereload({
       watchDirs: [
         path.join(this.crowi.viewsDir),
         path.join(this.crowi.publicDir),
@@ -41,6 +47,20 @@ class CrowiDev {
     }));
   }
 
+  loadPlugins(app) {
+    if (process.env.PLUGIN_NAMES_TOBE_LOADED !== undefined
+        && process.env.PLUGIN_NAMES_TOBE_LOADED.length > 0) {
+
+      const pluginNames = process.env.PLUGIN_NAMES_TOBE_LOADED.split(',');
+
+      // merge and remove duplicates
+      if (pluginNames.length > 0) {
+        var PluginService = require('../plugins/plugin.service');
+        var pluginService = new PluginService(this.crowi, app);
+        pluginService.loadPlugins(pluginNames);
+      }
+    }
+  }
 }
 
 module.exports = CrowiDev

+ 16 - 5
lib/crowi/index.js

@@ -295,6 +295,13 @@ Crowi.prototype.start = function() {
     , server
     , io;
 
+  // init CrowiDev
+  if (self.node_env === 'development') {
+    const CrowiDev = require('./dev');
+    this.crowiDev = new CrowiDev(self);
+    this.crowiDev.init();
+  }
+
   return Promise.resolve()
     .then(function() {
       return self.init()
@@ -307,11 +314,9 @@ Crowi.prototype.start = function() {
         console.log(`[${self.node_env}] Express server listening on port ${self.port}`);
       });
 
-      // setup Live Reload Tools
+      // setup
       if (self.node_env === 'development') {
-        const CrowiDev = require('./dev');
-        const crowiDev = new CrowiDev(self, server, app);
-        crowiDev.setupTools();
+        self.crowiDev.setup(server, app);
       }
 
       io = require('socket.io')(server);
@@ -341,7 +346,13 @@ Crowi.prototype.buildServer = function() {
   var isEnabledPlugins = Config.isEnabledPlugins(this.config);
   if (isEnabledPlugins) {
     debug('plugins enabled');
-    require('../plugins')(this, app);
+    var PluginService = require('../plugins/plugin.service');
+    var pluginService = new PluginService(this, app);
+    pluginService.autoDetectAndLoadPlugins();
+
+    if (env == 'development') {
+      this.crowiDev.loadPlugins(app);
+    }
   }
 
   if (env == 'development') {

+ 0 - 7
lib/plugins/index.js

@@ -1,7 +0,0 @@
-const debug = require('debug')('crowi:plugins');
-const PluginService = require('./plugin.service');
-
-module.exports = function(crowi, app) {
-  const pluginService = new PluginService(crowi, app);
-  pluginService.loadPlugins();
-}

+ 40 - 0
lib/plugins/plugin-utils-v2.js

@@ -0,0 +1,40 @@
+const path = require('path');
+
+class PluginUtilsV2 {
+
+  /**
+   * return a definition objects that has following structure:
+   *
+   * {
+   *   name: 'crowi-plugin-X',
+   *   meta: require('crowi-plugin-X'),
+   *   entries: [
+   *     'crowi-plugin-X/lib/client-entry'
+   *   ]
+   * }
+   *
+   *
+   * @param {string} pluginName
+   * @return
+   * @memberOf PluginService
+   */
+  generatePluginDefinition(name, isForClient = false) {
+    const meta = require(name);
+    let entries = (isForClient) ? meta.clientEntries : meta.serverEntries;
+
+    entries = entries.map((entryPath) => {
+      const moduleRoot = path.resolve(require.resolve(`${name}/package.json`), '..');
+      const entryRelativePath = path.relative(moduleRoot, entryPath);
+      return path.join(name, entryRelativePath);
+    });
+
+    return {
+      name,
+      meta,
+      entries,
+    }
+  }
+
+}
+
+module.exports = PluginUtilsV2

+ 17 - 6
lib/plugins/plugin-utils.js

@@ -1,6 +1,10 @@
 const path = require('path');
 const fs = require('graceful-fs');
 
+const PluginUtilsV2 = require('./plugin-utils-v2');
+
+const pluginUtilsV2 = new PluginUtilsV2();
+
 class PluginUtils {
 
   /**
@@ -14,20 +18,27 @@ class PluginUtils {
    *   ]
    * }
    *
-   *
    * @param {string} pluginName
    * @return
    * @memberOf PluginService
    */
   generatePluginDefinition(name, isForClient = false) {
     const meta = require(name);
-    const entries = (isForClient) ? meta.clientEntries : meta.serverEntries;
+    let definition;
 
-    return {
-      name,
-      meta,
-      entries,
+    switch (meta.pluginSchemaVersion) {
+      // v1 is deprecated
+      case 1:
+        console.log('pluginSchemaVersion 1 is deprecated');
+        debug('pluginSchemaVersion 1 is deprecated');
+        break;
+      // v2 or above
+      case 2:
+      default:
+        definition = pluginUtilsV2.generatePluginDefinition(name, isForClient);
     }
+
+    return definition;
   }
 
   /**

+ 19 - 15
lib/plugins/plugin.service.js

@@ -9,13 +9,17 @@ class PluginService {
     this.pluginUtils = new PluginUtils();
   }
 
+  autoDetectAndLoadPlugins() {
+    this.loadPlugins(this.pluginUtils.listPluginNames(this.crowi.rootDir));
+  }
+
   /**
    * load plugins
    *
    * @memberOf PluginService
    */
-  loadPlugins() {
-    this.pluginUtils.listPluginNames(this.crowi.rootDir)
+  loadPlugins(pluginNames) {
+    pluginNames
       .map((name) => {
         return this.pluginUtils.generatePluginDefinition(name);
       })
@@ -27,21 +31,21 @@ class PluginService {
   loadPlugin(definition) {
     const meta = definition.meta;
 
-    // v1 is deprecated
-    if (1 === meta.pluginSchemaVersion) {
-      debug('pluginSchemaVersion 1 is deprecated');
-      return;
+    switch (meta.pluginSchemaVersion) {
+      // v1 is deprecated
+      case 1:
+        console.warn('pluginSchemaVersion 1 is deprecated');
+        debug('pluginSchemaVersion 1 is deprecated');
+        break;
+      // v2 or above
+      default:
+        debug(`load plugin '${definition.name}'`);
+        definition.entries.forEach((entryPath) => {
+          const entry = require(entryPath);
+          entry(this.crowi, this.app);
+        });
     }
 
-    // v2
-    if (2 === meta.pluginSchemaVersion) {
-      debug(`load plugin '${definition.name}'`);
-
-      definition.entries.forEach((entryPath) => {
-        const entry = require(entryPath);
-        entry(this.crowi, this.app);
-      });
-    }
   }
 }
 

+ 6 - 4
package.json

@@ -28,11 +28,11 @@
     "clean": "npm run clean:js && npm run clean:dll",
     "mkdirp": "mkdirp",
     "plugin:def": "node bin/generate-plugin-definitions-source.js",
-    "prebuild:dev": "npm run plugin:def",
+    "prebuild:dev": "env-cmd config/env.dev.js npm run plugin:def",
     "prebuild:prod": "npm run plugin:def",
     "prestart": "npm run build:prod",
-    "server:dev:watch": "node-dev app.js --watch",
-    "server:dev": "node app.js",
+    "server:dev:watch": "env-cmd config/env.dev.js node-dev app.js --watch",
+    "server:dev": "env-cmd config/env.dev.js node app.js",
     "server:prod:container": "node app.js --production --container",
     "server:prod:onpremise": "mkdirp logs && node app.js --production --onpremise",
     "server:prod": "npm run server:prod:onpremise",
@@ -63,6 +63,7 @@
     "consolidate": "~0.14.0",
     "cookie-parser": "~1.3.4",
     "copy-webpack-plugin": "^4.0.0",
+    "crowi-pluginkit": "^1.1.0",
     "csrf": "~3.0.3",
     "css-loader": "^0.27.3",
     "debug": "~2.6.0",
@@ -70,6 +71,7 @@
     "diff2html": "^2.3.0",
     "elasticsearch": "^12.1.3",
     "emojify.js": "^1.1.0",
+    "env-cmd": "^5.0.0",
     "errorhandler": "~1.3.4",
     "express": "~4.15.2",
     "express-form": "~0.12.0",
@@ -96,6 +98,7 @@
     "node-sass": "^4.5.0",
     "nodemailer": "~2.7.0",
     "nodemailer-ses-transport": "~1.5.0",
+    "normalize-path": "^2.1.1",
     "optimize-js-plugin": "0.0.4",
     "react": "^15.4.2",
     "react-dom": "^15.4.2",
@@ -103,7 +106,6 @@
     "reveal.js": "~3.4.0",
     "rimraf": "^2.6.1",
     "sass-loader": "^6.0.3",
-    "slash": "^1.0.0",
     "socket.io": "~1.7.0",
     "socket.io-client": "~1.7.0",
     "sprintf": "~0.1.5",

+ 13 - 5
resource/js/legacy/crowi-form.js

@@ -75,17 +75,25 @@ $(function() {
 
   function renderPreview() {
     var markdown = $('#form-body').val();
-    var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
     // create context object
     var context = {
       markdown,
-      parsedHTML,
       currentPagePath: decodeURIComponent(location.pathname)
     };
 
-    // process interceptors for pre rendering
-    crowi.interceptorManager.process('preRenderPreview', context)   // process with the context
+    crowi.interceptorManager.process('preRenderPreview', context)
+      .then(() => crowi.interceptorManager.process('prePreProcess', context))
+      .then(() => {
+        context.markdown = crowiRenderer.preProcess(context.markdown);
+      })
+      .then(() => crowi.interceptorManager.process('postPreProcess', context))
+      .then(() => {
+        var parsedHTML = crowiRenderer.render(context.markdown, rendererOptions);
+        context['parsedHTML'] = parsedHTML;
+      })
+      .then(() => crowi.interceptorManager.process('postRenderPreview', context))
+      .then(() => crowi.interceptorManager.process('preRenderPreviewHtml', context))
       // render HTML with jQuery
       .then(() => {
         $('#preview-body').html(context.parsedHTML);
@@ -94,7 +102,7 @@ $(function() {
       // process interceptors for post rendering
       .then((bodyElement) => {
         context = Object.assign(context, {bodyElement})
-        return crowi.interceptorManager.process('postRenderPreview', context);
+        return crowi.interceptorManager.process('postRenderPreviewHtml', context);
       });
   }
 

+ 14 - 5
resource/js/legacy/crowi.js

@@ -426,17 +426,26 @@ $(function() {
     var $rawTextOriginal = $('#raw-text-original');
     if ($rawTextOriginal.length > 0) {
       var markdown = Crowi.unescape($('#raw-text-original').html());
-      var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
       // create context object
       var context = {
         markdown,
-        parsedHTML,
         currentPagePath: decodeURIComponent(location.pathname)
       };
 
-      // process interceptors for pre rendering
-      crowi.interceptorManager.process('preRender', context)   // process with the context
+      crowi.interceptorManager.process('preRender', context)
+        .then(() => crowi.interceptorManager.process('prePreProcess', context))
+        .then(() => {
+          context.markdown = crowiRenderer.preProcess(context.markdown);
+        })
+        .then(() => crowi.interceptorManager.process('postPreProcess', context))
+        .then(() => {
+          var parsedHTML = crowiRenderer.parseMarkdown(context.markdown, rendererOptions);
+          context.parsedHTML = parsedHTML;
+          Promise.resolve(context);
+        })
+        .then(() => crowi.interceptorManager.process('postRender', context))
+        .then(() => crowi.interceptorManager.process('preRenderHtml', context))
         // render HTML with jQuery
         .then(() => {
           $('#revision-body-content').html(context.parsedHTML);
@@ -458,7 +467,7 @@ $(function() {
         // process interceptors for post rendering
         .then((bodyElement) => {
           context = Object.assign(context, {bodyElement})
-          return crowi.interceptorManager.process('postRender', context);
+          return crowi.interceptorManager.process('postRenderHtml', context);
         });
 
 

+ 10 - 8
resource/js/plugin.js

@@ -22,17 +22,19 @@ export default class CrowiPlugin {
 
     definitions.forEach((definition) => {
       const meta = definition.meta;
-      const entries = definition.entries;
 
-      // v1 is deprecated
-
-      // v2
-      if (2 === meta.pluginSchemaVersion) {
-        entries.forEach((entry) => {
-          entry(crowi, crowiRenderer);
-        });
+      switch (meta.pluginSchemaVersion) {
+        // v1 is deprecated
+        case 1:
+          break;
+        // v2 or above
+        default:
+          definition.entries.forEach((entry) => {
+            entry(crowi, crowiRenderer);
+          });
       }
     });
+
   }
 
 }

+ 8 - 0
resource/js/util/Crowi.js

@@ -4,6 +4,10 @@
 
 import axios from 'axios'
 import InterceptorManager from '../../../lib/util/interceptorManager';
+import {
+  DetachCodeBlockInterceptor,
+  RestoreCodeBlockInterceptor,
+} from './Interceptor/DetachCodeBlock';
 
 export default class Crowi {
   constructor(context, window) {
@@ -19,6 +23,10 @@ export default class Crowi {
     this.apiRequest = this.apiRequest.bind(this);
 
     this.interceptorManager = new InterceptorManager();
+    this.interceptorManager.addInterceptors([
+      new DetachCodeBlockInterceptor(this),
+      new RestoreCodeBlockInterceptor(this),
+    ]);
 
     // FIXME
     this.me = context.me;

+ 0 - 1
resource/js/util/CrowiRenderer.js

@@ -131,7 +131,6 @@ export default class CrowiRenderer {
   render(markdown, rendererOptions) {
     let html = '';
 
-    markdown = this.preProcess(markdown);
     html = this.parseMarkdown(markdown, rendererOptions.marked || {});
 
     return html;

+ 116 - 0
resource/js/util/Interceptor/DetachCodeBlock.js

@@ -0,0 +1,116 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import { BasicInterceptor } from 'crowi-pluginkit';
+
+
+class DetachCodeBlockUtil {
+  static createReplaceStr(replaceId) {
+    return `<pre>${replaceId}</pre>`;
+  }
+}
+
+/**
+ * The interceptor that detach code blocks
+ */
+export class DetachCodeBlockInterceptor extends BasicInterceptor {
+
+  constructor(crowi) {
+    super();
+    this.crowi = crowi;
+    this.crowiForJquery = crowi.getCrowiForJquery();
+  }
+
+  /**
+   * @inheritdoc
+   */
+  isInterceptWhen(contextName) {
+    return (
+      contextName === 'prePreProcess'
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  process(contextName, ...args) {
+    const context = Object.assign(args[0]);   // clone
+    const markdown = context.markdown;
+    const currentPagePath = context.currentPagePath;
+
+    context.dcbContextMap = {};
+
+    // see: https://regex101.com/r/8PAEcC/1
+    context.markdown = markdown.replace(/```(.|[\r\n])*?```/gm, (all) => {
+      // create ID
+      const replaceId = 'dcb-' + this.createRandomStr(8);
+
+      // register to context
+      let dcbContext = {};
+      dcbContext.content = all;
+      dcbContext.substituteContent = DetachCodeBlockUtil.createReplaceStr(replaceId);
+      context.dcbContextMap[replaceId] = dcbContext;
+
+      // return substituteContent
+      return dcbContext.substituteContent;
+    });
+
+    // resolve
+    return Promise.resolve(context);
+  }
+
+  /**
+   * @see http://qiita.com/ryounagaoka/items/4736c225bdd86a74d59c
+   *
+   * @param {number} length
+   * @return random strings
+   */
+  createRandomStr(length) {
+    const bag = "abcdefghijklmnopqrstuvwxyz0123456789";
+    let generated = "";
+    for (var i = 0; i < length; i++) {
+      generated += bag[Math.floor(Math.random() * bag.length)];
+    }
+    return generated;
+  }
+}
+
+
+/**
+ * The interceptor that restore detached code blocks
+ */
+export class RestoreCodeBlockInterceptor extends BasicInterceptor {
+
+  constructor(crowi) {
+    super();
+    this.crowi = crowi;
+    this.crowiForJquery = crowi.getCrowiForJquery();
+  }
+
+  /**
+   * @inheritdoc
+   */
+  isInterceptWhen(contextName) {
+    return (
+      contextName === 'postPreProcess'
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  process(contextName, ...args) {
+    const context = Object.assign(args[0]);   // clone
+
+    // forEach keys of dcbContextMap
+    Object.keys(context.dcbContextMap).forEach((replaceId) => {
+      // get context object from context
+      let dcbContext = context.dcbContextMap[replaceId];
+
+      context.markdown = context.markdown.replace(dcbContext.substituteContent, dcbContext.content);
+    });
+
+    // resolve
+    return Promise.resolve(context);
+  }
+}

+ 56 - 28
yarn.lock

@@ -138,8 +138,8 @@ arr-diff@^2.0.0:
     arr-flatten "^1.0.1"
 
 arr-flatten@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.2.tgz#1ec1e63439c54f67d6f72bb4299c3d4f73b2d996"
 
 array-back@^1.0.2, array-back@^1.0.3, array-back@^1.0.4:
   version "1.0.4"
@@ -1095,8 +1095,8 @@ caniuse-api@^1.5.2:
     lodash.uniq "^4.5.0"
 
 caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
-  version "1.0.30000649"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
+  version "1.0.30000655"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000655.tgz#e40b6287adc938848d6708ef83d65b5f54ac1874"
 
 cardinal@^1.0.0:
   version "1.0.0"
@@ -1548,6 +1548,18 @@ cross-spawn@^3.0.0:
     lru-cache "^4.0.1"
     which "^1.2.9"
 
+cross-spawn@^5.0.1:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+  dependencies:
+    lru-cache "^4.0.1"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+crowi-pluginkit@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/crowi-pluginkit/-/crowi-pluginkit-1.1.0.tgz#c423f812a1d5198f57ba0180230b6be53120595d"
+
 cryptiles@2.x.x:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@@ -1675,8 +1687,8 @@ dashdash@^1.12.0:
     assert-plus "^1.0.0"
 
 date-fns@^1.23.0:
-  version "1.28.2"
-  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.2.tgz#19e4192d68875c0bf7c9537e3f296a8ec64853ef"
+  version "1.28.3"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.3.tgz#145d87adc3f5a82c6bda668de97eee1132c97ea1"
 
 date-now@1.0.1:
   version "1.0.1"
@@ -1942,6 +1954,12 @@ enhanced-resolve@^3.0.0:
     object-assign "^4.0.1"
     tapable "^0.2.5"
 
+env-cmd@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-5.0.0.tgz#c10892176a18bfcc5658cd9a2880add281de31ce"
+  dependencies:
+    cross-spawn "^5.0.1"
+
 errno@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -2621,8 +2639,8 @@ hooks-fixed@1.2.0:
   resolved "https://registry.yarnpkg.com/hooks-fixed/-/hooks-fixed-1.2.0.tgz#0d2772d4d7d685ff9244724a9f0b5b2559aac96b"
 
 hosted-git-info@^2.1.4:
-  version "2.4.1"
-  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.1.tgz#4b0445e41c004a8bd1337773a4ff790ca40318c8"
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67"
 
 html-comment-regex@^1.1.0:
   version "1.1.1"
@@ -2684,8 +2702,8 @@ https-proxy-agent@^1.0.0:
     extend "3"
 
 i18next-express-middleware@~1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/i18next-express-middleware/-/i18next-express-middleware-1.0.3.tgz#46e080c6589503ce55d7bbfb68ff717727716c93"
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/i18next-express-middleware/-/i18next-express-middleware-1.0.4.tgz#e17c1ea58d1e5719d1f9a2375ec16fec0a69ec38"
   dependencies:
     cookies "0.6.1"
 
@@ -3699,8 +3717,8 @@ mustache@~2.2.1:
   resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.2.1.tgz#2c40ca21c278f53150682bcf9090e41a3339b876"
 
 nan@^2.3.0, nan@^2.3.2:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01"
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
 
 native-promise-only@^0.8.1:
   version "0.8.1"
@@ -3934,7 +3952,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
     semver "2 || 3 || 4 || 5"
     validate-npm-package-license "^3.0.1"
 
-normalize-path@^2.0.1:
+normalize-path@^2.0.1, normalize-path@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
   dependencies:
@@ -4447,8 +4465,8 @@ postcss-zindex@^2.0.1:
     uniqs "^2.0.0"
 
 postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
-  version "5.2.16"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.16.tgz#732b3100000f9ff8379a48a53839ed097376ad57"
+  version "5.2.17"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b"
   dependencies:
     chalk "^1.1.3"
     js-base64 "^2.1.9"
@@ -4481,9 +4499,9 @@ promise@^7.1.1:
   dependencies:
     asap "~2.0.3"
 
-prop-types@^15.5.2, prop-types@~15.5.0:
-  version "15.5.6"
-  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.6.tgz#797a915b1714b645ebb7c5d6cc690346205bd2aa"
+prop-types@^15.5.7, prop-types@~15.5.7:
+  version "15.5.8"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
   dependencies:
     fbjs "^0.8.9"
 
@@ -4596,22 +4614,22 @@ rc@^1.1.7:
     strip-json-comments "~2.0.1"
 
 react-dom@^15.4.2:
-  version "15.5.3"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.5.3.tgz#2ee127ce942df55da53111ae303316e68072b5c5"
+  version "15.5.4"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.5.4.tgz#ba0c28786fd52ed7e4f2135fe0288d462aef93da"
   dependencies:
     fbjs "^0.8.9"
     loose-envify "^1.1.0"
     object-assign "^4.1.0"
-    prop-types "~15.5.0"
+    prop-types "~15.5.7"
 
 react@^15.4.2:
-  version "15.5.3"
-  resolved "https://registry.yarnpkg.com/react/-/react-15.5.3.tgz#84055382c025dec4e3b902bb61a8697cc79c1258"
+  version "15.5.4"
+  resolved "https://registry.yarnpkg.com/react/-/react-15.5.4.tgz#fa83eb01506ab237cdc1c8c3b1cea8de012bf047"
   dependencies:
     fbjs "^0.8.9"
     loose-envify "^1.1.0"
     object-assign "^4.1.0"
-    prop-types "^15.5.2"
+    prop-types "^15.5.7"
 
 read-pkg-up@^1.0.1:
   version "1.0.1"
@@ -5076,6 +5094,16 @@ shallow-clone@^0.1.2:
     lazy-cache "^0.2.3"
     mixin-object "^2.0.1"
 
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+
 shellwords@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14"
@@ -5238,8 +5266,8 @@ sprintf@~0.1.5:
   resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf"
 
 sshpk@^1.7.0:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.11.0.tgz#2d8d5ebb4a6fab28ffba37fa62a90f4a3ea59d77"
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
   dependencies:
     asn1 "~0.2.3"
     assert-plus "^1.0.0"
@@ -5657,8 +5685,8 @@ verror@1.3.6:
     extsprintf "1.0.2"
 
 vlq@^0.2.1:
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.1.tgz#14439d711891e682535467f8587c5630e4222a6c"
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1"
 
 vm-browserify@0.0.4:
   version "0.0.4"