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

feat: locales availability + IE display mode

Nick 5 лет назад
Родитель
Сommit
b6f1e1805e

+ 55 - 2
.babelrc

@@ -16,13 +16,66 @@
     "@babel/plugin-proposal-function-sent",
     "@babel/plugin-proposal-export-namespace-from",
     "@babel/plugin-proposal-numeric-separator",
-    "@babel/plugin-proposal-throw-expressions"
+    "@babel/plugin-proposal-throw-expressions",
+    [
+      "prismjs", {
+        "languages": [
+          "markup",
+          "css",
+          "clike",
+          "javascript",
+          "c",
+          "bash",
+          "basic",
+          "cpp",
+          "csharp",
+          "arduino",
+          "ruby",
+          "elixir",
+          "fsharp",
+          "go",
+          "graphql",
+          "handlebars",
+          "haskell",
+          "ini",
+          "java",
+          "json",
+          "kotlin",
+          "latex",
+          "less",
+          "makefile",
+          "markdown",
+          "matlab",
+          "nginx",
+          "objectivec",
+          "perl",
+          "php",
+          "powershell",
+          "pug",
+          "python",
+          "typescript",
+          "rust",
+          "scss",
+          "scala",
+          "smalltalk",
+          "sql",
+          "stylus",
+          "swift",
+          "vbnet",
+          "yaml"
+        ],
+        "plugins": ["line-numbers"],
+        "theme": "dark",
+        "css": true
+      }
+    ]
   ],
   "presets": [
     [
       "@babel/preset-env", {
         "useBuiltIns": "entry",
-        "corejs": 3
+        "corejs": 3,
+        "debug": true
       }
     ]
   ]

+ 1 - 0
.gitignore

@@ -19,6 +19,7 @@ npm-debug.log*
 # Generated assets
 /assets
 server/views/master.pug
+server/views/legacy.pug
 server/views/setup.pug
 
 # Webpack

+ 70 - 23
client/components/admin/admin-locale.vue

@@ -52,8 +52,6 @@
                 v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                     .subheading {{ $t('admin:locale.namespacing') }}
-                  v-spacer
-                  v-chip(label, color='white', small).primary--text coming soon
                 v-card-text
                   v-switch(
                     v-model='namespacing'
@@ -102,28 +100,35 @@
               v-card.animated.fadeInUp.wait-p4s
                 v-toolbar(color='teal', dark, dense, flat)
                   v-toolbar-title
-                    .subheading {{ $t('admin:locale.download') }}
-                v-list(two-line, dense)
-                  template(v-for='(lc, idx) in locales')
-                    v-list-tile(:key='lc.code')
-                      v-list-tile-avatar
-                        v-avatar.teal.white--text(size='40') {{lc.code.toUpperCase()}}
-                      v-list-tile-content
-                        v-list-tile-title(v-html='lc.name')
-                        v-list-tile-sub-title(v-html='lc.nativeName')
-                      v-list-tile-action(v-if='lc.isRTL')
-                        v-chip(label, small, :class='$vuetify.dark ? `text--lighten-5` : `text--darken-2`').caption.grey--text RTL
-                      v-list-tile-action(v-if='lc.isInstalled && lc.installDate < lc.updatedAt')
-                        v-btn(icon, @click='download(lc)')
-                          v-icon.blue--text cached
-                      v-list-tile-action(v-else-if='lc.isInstalled')
+                    .subheading {{ $t('admin:locale.downloadTitle') }}
+                v-data-table(
+                  :headers='headers',
+                  :items='locales',
+                  hide-actions,
+                  item-key='code',
+                  :rows-per-page-items='[-1]'
+                )
+                  template(v-slot:items='lc')
+                    td
+                      v-chip.white--text(label, color='teal', small) {{lc.item.code}}
+                    td
+                      strong {{lc.item.name}}
+                    td
+                      span {{ lc.item.nativeName }}
+                    td.text-xs-center
+                      v-icon(v-if='lc.item.isRTL') check
+                    td
+                      .d-flex.align-center.pl-4
+                        .caption.mr-2(:class='lc.item.availability <= 33 ? `red--text` : (lc.item.availability <= 66) ? `orange--text` : `green--text`') {{lc.item.availability}}%
+                        v-progress-circular(:value='lc.item.availability', width='2', size='20', :color='lc.item.availability <= 33 ? `red` : (lc.item.availability <= 66) ? `orange` : `green`')
+                    td.text-xs-center
+                      v-progress-circular(v-if='lc.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
+                      v-btn(v-else-if='lc.item.isInstalled && lc.item.installDate < lc.item.updatedAt', icon, @click='download(lc.item)')
+                        v-icon.blue--text cached
+                      v-btn(v-else-if='lc.item.isInstalled', icon, @click='download(lc.item)')
                         v-icon.green--text check
-                      v-list-tile-action(v-else-if='lc.isDownloading')
-                        v-progress-circular(indeterminate, color='blue', size='20', :width='3')
-                      v-list-tile-action(v-else)
-                        v-btn(icon, @click='download(lc)')
-                          v-icon.grey--text cloud_download
-                    v-divider.my-0(inset, v-if='idx < locales.length - 1')
+                      v-btn(v-else, icon, @click='download(lc.item)')
+                        v-icon.grey--text cloud_download
               v-card.wiki-form.mt-3.animated.fadeInUp.wait-p5s
                 v-toolbar(color='teal', dark, dense, flat)
                   v-toolbar-title
@@ -158,6 +163,46 @@ export default {
   computed: {
     installedLocales() {
       return _.filter(this.locales, ['isInstalled', true])
+    },
+    headers() {
+      return [
+        {
+          text: this.$t('admin:locale.code'),
+          align: 'left',
+          value: 'code',
+          width: 10
+        },
+        {
+          text: this.$t('admin:locale.name'),
+          align: 'left',
+          value: 'name'
+        },
+        {
+          text: this.$t('admin:locale.nativeName'),
+          align: 'left',
+          value: 'nativeName'
+        },
+        {
+          text: this.$t('admin:locale.rtl'),
+          align: 'center',
+          value: 'isRTL',
+          sortable: false,
+          width: 10
+        },
+        {
+          text: this.$t('admin:locale.availability'),
+          align: 'center',
+          value: 'availability',
+          width: 100
+        },
+        {
+          text: this.$t('admin:locale.download'),
+          align: 'center',
+          value: 'code',
+          sortable: false,
+          width: 100
+        }
+      ]
     }
   },
   methods: {
@@ -173,6 +218,8 @@ export default {
       if (resp.succeeded) {
         lc.isDownloading = false
         lc.isInstalled = true
+        lc.updatedAt = new Date().toISOString()
+        lc.installDate = lc.updatedAt
         this.$store.commit('showNotification', {
           message: `Locale ${lc.name} has been installed successfully.`,
           style: 'success',

+ 2 - 2
client/components/admin/admin-utilities-auth.vue

@@ -3,14 +3,14 @@
     v-toolbar(flat, color='primary', dark, dense)
       .subheading {{ $t('admin:utilities.authTitle') }}
     v-card-text
-      v-subheader.pl-0 Generate New Authentication Public / Private Key Certificates
+      v-subheader.pl-0.primary--text Generate New Authentication Public / Private Key Certificates
       .body-1 This will invalidate all current session tokens and cause all users to be logged out.
       .body-1.red--text You will need to log back in after the operation.
       v-btn(outline, color='primary', @click='regenCerts', :disabled='loading').ml-0.mt-3
         v-icon(left) build
         span Proceed
       v-divider.my-3
-      v-subheader.pl-0 Reset Guest User
+      v-subheader.pl-0.primary--text Reset Guest User
       .body-1 This will reset the guest user to its default parameters and permissions.
       v-btn(outline, color='primary', @click='resetGuest', :disabled='loading').ml-0.mt-3
         v-icon(left) build

+ 2 - 2
client/components/admin/admin-utilities-cache.vue

@@ -3,13 +3,13 @@
     v-toolbar(flat, color='primary', dark, dense)
       .subheading {{ $t('admin:utilities.cacheTitle') }}
     v-card-text
-      v-subheader.pl-0 Flush Pages and Assets Cache
+      v-subheader.pl-0.primary--text Flush Pages and Assets Cache
       .body-1 Pages and Assets are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again.
       v-btn(outline, color='primary', @click='flushCache', :disabled='loading').ml-0.mt-3
         v-icon(left) build
         span Proceed
       v-divider.my-3
-      v-subheader.pl-0 Flush Temporary Uploads
+      v-subheader.pl-0.primary--text Flush Temporary Uploads
       .body-1 New uploads are temporarily saved to disk while they are being processed. They are automatically deleted after processing, but you can force an immediate cleanup using this tool.
       .body-1.red--text Note that performing this action while an upload is in progress can result in a failed upload.
       v-btn(outline, color='primary', @click='flushUploads', :disabled='loading').ml-0.mt-3

+ 68 - 0
client/components/admin/admin-utilities-content.vue

@@ -0,0 +1,68 @@
+<template lang='pug'>
+  v-card
+    v-toolbar(flat, color='primary', dark, dense)
+      .subheading {{ $t('admin:utilities.contentTitle') }}
+    v-card-text
+      v-subheader.pl-0.primary--text Migrate all pages to base language
+      .body-1 If you created content before selecting a different locale and activating the namespacing capabilities, you may want to transfer all content to the base locale.
+      .body-1.red--text: strong This operation is destructive and cannot be reversed! Make sure you have proper backups!
+      .body-1.mt-3 Based on your current configuration, all pages will be migrated to the locale #[v-chip(label, small): strong {{currentLocale.toUpperCase()}}]
+      .body-1.mt-3 Pages that are already in the target locale will not be touched. If a page already exists at the target, the source page will not be modified as it would create a conflict. If you want to overwrite the target content, you must first delete that page.
+      v-btn(outline, color='primary', @click='migrateToLocale', :disabled='loading').ml-0.mt-3
+        v-icon(left) build
+        span Proceed
+</template>
+
+<script>
+import _ from 'lodash'
+import utilityContentMigrateLocaleMutation from 'gql/admin/utilities/utilities-mutation-content-migratelocale.gql'
+
+/* global siteLang */
+
+export default {
+  data: () => {
+    return {
+      loading: false
+    }
+  },
+  computed: {
+    currentLocale() {
+      return siteConfig.lang
+    }
+  },
+  methods: {
+    async migrateToLocale() {
+      this.loading = true
+      this.$store.commit(`loadingStart`, 'admin-utilities-content-migratelocale')
+
+      try {
+        const respRaw = await this.$apollo.mutate({
+          mutation: utilityContentMigrateLocaleMutation,
+          variables: {
+            targetLocale: siteConfig.lang
+          }
+        })
+        const resp = _.get(respRaw, 'data.pages.migrateToLocale.responseResult', {})
+        if (resp.succeeded) {
+          this.$store.commit('showNotification', {
+            message: 'Migrated all content to target locale successfully.',
+            style: 'success',
+            icon: 'check'
+          })
+        } else {
+          throw new Error(resp.message)
+        }
+      } catch (err) {
+        this.$store.commit('pushGraphError', err)
+      }
+
+      this.$store.commit(`loadingStop`, 'admin-utilities-content-migratelocale')
+      this.loading = false
+    }
+  }
+}
+</script>
+
+<style lang='scss'>
+
+</style>

+ 7 - 0
client/components/admin/admin-utilities.vue

@@ -35,6 +35,7 @@
 export default {
   components: {
     UtilityAuth: () => import(/* webpackChunkName: "admin" */ './admin-utilities-auth.vue'),
+    UtilityContent: () => import(/* webpackChunkName: "admin" */ './admin-utilities-content.vue'),
     UtilityCache: () => import(/* webpackChunkName: "admin" */ './admin-utilities-cache.vue'),
     UtilityImportv1: () => import(/* webpackChunkName: "admin" */ './admin-utilities-importv1.vue'),
     UtilityTelemetry: () => import(/* webpackChunkName: "admin" */ './admin-utilities-telemetry.vue')
@@ -49,6 +50,12 @@ export default {
           i18nKey: 'auth',
           isAvailable: true
         },
+        {
+          key: 'UtilityContent',
+          icon: 'insert_drive_file',
+          i18nKey: 'content',
+          isAvailable: true
+        },
         {
           key: 'UtilityCache',
           icon: 'invert_colors',

+ 2 - 2
client/components/common/search-results.vue

@@ -91,7 +91,7 @@ export default {
       return this.response.suggestions ? this.response.suggestions : []
     },
     paginationLength() {
-      return this.response.totalHits > 0 ? 0 : Math.ceil(this.response.totalHits / 10)
+      return (this.response.totalHits > 0) ? 0 : Math.ceil(this.response.totalHits / 10)
     }
   },
   watch: {
@@ -107,7 +107,7 @@ export default {
   },
   mounted() {
     this.$root.$on('searchMove', (dir) => {
-      this.cursor += (dir === 'up' ? -1 : 1)
+      this.cursor += ((dir === 'up') ? -1 : 1)
       if (this.cursor < -1) {
         this.cursor = -1
       } else if (this.cursor > this.results.length + this.suggestions.length - 1) {

+ 1 - 1
client/components/editor/editor-markdown.vue

@@ -190,7 +190,7 @@ import mdMark from 'markdown-it-mark'
 import mdImsize from 'markdown-it-imsize'
 
 // Prism (Syntax Highlighting)
-import Prism from '@/libs/prism/prism.js'
+import Prism from 'prismjs'
 
 // ========================================
 // INIT

+ 1 - 0
client/graph/admin/locale/locale-query-list.gql

@@ -1,6 +1,7 @@
 {
   localization {
     locales {
+      availability
       code
       createdAt
       isInstalled

+ 12 - 0
client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql

@@ -0,0 +1,12 @@
+mutation {
+  pages {
+    migrateToLocale {
+      responseResult {
+        succeeded
+        errorCode
+        slug
+        message
+      }
+    }
+  }
+}

+ 3 - 0
client/index-legacy.js

@@ -0,0 +1,3 @@
+require('./scss/legacy.scss')
+
+window.WIKI = null

+ 2 - 1
client/index-setup.js

@@ -1,4 +1,5 @@
-require('@babel/polyfill')
+require('core-js/stable')
+require('regenerator-runtime/runtime')
 
 require('vuetify/src/stylus/main.styl')
 require('./scss/app.scss')

+ 1 - 1
client/scss/app.scss

@@ -18,7 +18,7 @@
 @import 'layout/md2';
 
 // @import '../libs/twemoji/twemoji-awesome';
-@import '../libs/prism/prism.css';
+// @import '../libs/prism/prism.css';
 @import '~vue-tour/dist/vue-tour.css';
 @import '~vue-status-indicator/styles.css';
 @import '~xterm/dist/xterm.css';

+ 218 - 0
client/scss/legacy.scss

@@ -0,0 +1,218 @@
+@import "global";
+
+@import "./base/fonts.scss";
+@import "./base/icons.scss";
+
+html {
+  box-sizing: border-box;
+  background-color: mc('grey', '50');
+  font-size: 14px;
+}
+*, *:before, *:after {
+  box-sizing: inherit;
+}
+* {
+  margin: 0;
+  padding: 0;
+}
+
+.is-hidden {
+  display: none;
+}
+
+body {
+  margin: 0;
+  padding: 0;
+  font-family: "Roboto",sans-serif;
+  line-height: 1.5;
+  min-height: 100vh;
+}
+
+.header {
+  background-color: #000;
+  color: #FFF;
+  height: 64px;
+  padding: 0 16px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  &-title {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 500;
+    letter-spacing: .02em;
+  }
+
+  &-deprecated {
+    color: mc('red', '100');
+
+    a {
+      color: mc('pink', '400');
+    }
+  }
+
+  &-login {
+    a {
+      text-decoration: none;
+      color: #FFF;
+      transition: color .3s ease;
+
+      &:hover {
+        color: mc('blue', '500');
+      }
+    }
+  }
+}
+
+.main {
+  display: flex;
+  align-items: stretch;
+  min-height: calc(100vh - 64px);
+  height: 50vh;
+
+  &-container {
+    flex-grow: 1;
+  }
+}
+
+.sidebar {
+  width: 300px;
+  background-color: mc('blue', '700');
+  color: #FFF;
+  padding: 8px 0;
+
+  .sidebar-link {
+    height: 40px;
+    font-size: 13px;
+    display: flex;
+    align-items: center;
+    padding: 0 16px;
+    transition: background .3s cubic-bezier(.25,.8,.5,1);
+    font-weight: 400;
+    color: #FFF;
+    text-decoration: none;
+
+    &:hover {
+      background: hsla(0,0%,100%,.08);
+    }
+  }
+
+  i.material-icons {
+    width: 56px;
+    padding-left: 8px;
+  }
+
+  .sidebar-divider {
+    border-top: 1px solid hsla(0,0%,100%,.12);
+    margin: 8px 0;
+  }
+
+  .sidebar-title {
+    font-size: 13px;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    padding: 0 16px 0 24px;
+    font-weight: 500;
+    color: hsla(0,0%,100%,.7);
+  }
+}
+
+.page-header {
+  background-color: mc('grey', '100');
+  padding: 0 24px;
+  height: 90px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid mc('grey', '200');
+
+  h1 {
+    font-size: 24px;
+    font-weight: 400;
+    line-height: 32px;
+    color: mc('grey', '800');
+  }
+
+  h2 {
+    color: mc('grey', '600');
+    font-size: 12px;
+    font-weight: 400;
+  }
+
+  &-left {
+    flex-grow: 1;
+  }
+
+  &-right {
+    flex: 0 0 324px;
+    padding-left: 16px;
+
+    &-title {
+      color: mc('grey', '500');
+      font-size: 12px;
+    }
+    &-author {
+      color: mc('grey', '800');
+      font-weight: 500;
+    }
+    &-updated {
+      color: mc('grey', '600');
+      font-size: 12px;
+    }
+  }
+}
+
+.page-contents {
+  display: flex;
+}
+
+.toc {
+  flex: 0 0 348px;
+  background-color: mc('grey', '200');
+  padding: 4px 0;
+
+  &-title {
+    font-size: 13px;
+    height: 40px;
+    display: flex;
+    color: mc('blue', '600');
+    align-items: center;
+    font-weight: 500;
+    padding: 0 16px;
+  }
+
+  &-tile {
+    text-decoration: none;
+    height: 40px;
+    display: flex;
+    font-size: 13px;
+    align-items: center;
+    padding: 0 16px;
+    color: mc('grey', '800');
+    transition: background-color .3s ease;
+
+    &.inset {
+      padding-left: 32px;
+    }
+
+    &:hover {
+      background-color: rgba(0,0,0,.06);
+    }
+  }
+
+  &-divider {
+    border-top: 1px solid rgba(0,0,0,.12);
+    margin: 0 0 0 24px;
+
+    &.inset {
+      margin-left: 40px;
+    }
+  }
+}
+
+@import "../themes/default/scss/app.scss";
+
+.contents {
+  flex-grow: 1;
+}

+ 1 - 1
client/themes/default/components/page.vue

@@ -137,7 +137,7 @@
 
 <script>
 import { StatusIndicator } from 'vue-status-indicator'
-import Prism from '@/libs/prism/prism.js'
+import Prism from 'prismjs'
 import { get } from 'vuex-pathify'
 import _ from 'lodash'
 

+ 127 - 0
dev/templates/legacy.pug

@@ -0,0 +1,127 @@
+doctype html
+html
+  head
+    meta(http-equiv='X-UA-Compatible', content='IE=edge')
+    meta(charset='UTF-8')
+    meta(name='viewport', content='user-scalable=yes, width=device-width, initial-scale=1, maximum-scale=5')
+    meta(name='theme-color', content='#333333')
+    meta(name='msapplication-TileColor', content='#333333')
+    meta(name='msapplication-TileImage', content='/favicons/ms-icon-144x144.png')
+
+    title= pageMeta.title + ' | ' + config.title
+
+    //- SEO / OpenGraph
+    meta(name='description', content=pageMeta.description)
+    meta(property='og:title', content=pageMeta.title)
+    meta(property='og:type', content='website')
+    meta(property='og:description', content=pageMeta.description)
+    meta(property='og:image', content=pageMeta.image)
+    meta(property='og:url', content=pageMeta.url)
+    meta(property='og:site_name', content=config.title)
+
+    //- Favicon
+    each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180]
+      link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href='/favicons/apple-icon-' + favsize + 'x' + favsize + '.png')
+    link(rel='icon', type='image/png', sizes='192x192', href='/favicons/android-icon-192x192.png')
+    each favsize in [32, 96, 16]
+      link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href='/favicons/favicon-' + favsize + 'x' + favsize + '.png')
+    link(rel='manifest', href='/manifest.json')
+
+    //- CSS
+    <% for (var index in htmlWebpackPlugin.files.css) { %>
+      <% if (htmlWebpackPlugin.files.cssIntegrity) { %>
+    link(
+      type='text/css'
+      rel='stylesheet'
+      href='<%= htmlWebpackPlugin.files.css[index] %>'
+      integrity='<%= htmlWebpackPlugin.files.cssIntegrity[index] %>'
+      crossorigin='<%= webpackConfig.output.crossOriginLoading %>'
+    )
+      <% } else { %>
+    link(
+      type='text/css'
+      rel='stylesheet'
+      href='<%= htmlWebpackPlugin.files.css[index] %>'
+    )
+      <% } %>
+    <% } %>
+
+    script(
+      crossorigin='anonymous'
+      src='https://polyfill.io/v3/polyfill.min.js?features=EventSource'
+    )
+
+    //- JS
+    <% for (var index in htmlWebpackPlugin.files.js) { %>
+      <% if (htmlWebpackPlugin.files.cssIntegrity) { %>
+    script(
+      type='text/javascript'
+      src='<%= htmlWebpackPlugin.files.js[index] %>'
+      integrity='<%= htmlWebpackPlugin.files.jsIntegrity[index] %>'
+      crossorigin='<%= webpackConfig.output.crossOriginLoading %>'
+      )
+      <% } else { %>
+    script(
+      type='text/javascript'
+      src='<%= htmlWebpackPlugin.files.js[index] %>'
+      )
+      <% } %>
+    <% } %>
+
+    != analyticsCode.head
+
+    if injectCode.css
+      style(type='text/css')!= injectCode.css
+    if injectCode.head
+      != injectCode.head
+
+  body
+    != analyticsCode.bodyStart
+    #root
+      .header
+        span.header-title= siteConfig.title
+        span.header-deprecated Your browser is outdated. Upgrade to a #[a(href='https://bestvpn.org/outdatedbrowser/en', rel='nofollow') modern browser].
+        span.header-login
+          a(href='/login')
+            i.material-icons account_circle
+      .main
+        .sidebar
+          each navItem in sidebar
+            if navItem.kind === 'link'
+              a.sidebar-link(href=navItem.target)
+                i.material-icons= navItem.icon
+                span= navItem.label
+            else if navItem.kind === 'divider'
+              .sidebar-divider
+            else if navItem.kind === 'header'
+              .sidebar-title= navItem.label
+        .main-container
+          .page-header
+            .page-header-left
+              h1= page.title
+              h2= page.description
+            .page-header-right
+              .page-header-right-title Last edited by
+              .page-header-right-author= page.authorName
+              .page-header-right-updated= page.updatedAt
+          .page-contents
+            .contents
+              div!= page.render
+            if page.toc.length
+              .toc
+                .toc-title Table of Contents
+                each tocItem, tocIdx in page.toc
+                  a.toc-tile(href='#' + tocItem.anchor)
+                    i.material-icons arrow_right
+                    span= tocItem.title
+                  if tocIdx < page.toc.length - 1 || tocItem.children.length
+                    .toc-divider
+                  each tocSubItem in tocItem.children
+                    a.toc-tile.inset(href='#' + tocSubItem.anchor)
+                      i.material-icons arrow_right
+                      span= tocSubItem.title
+                    if tocIdx < page.toc.length - 1
+                      .toc-divider.inset
+    if injectCode.body
+      != injectCode.body
+    != analyticsCode.bodyEnd

+ 10 - 2
dev/webpack/webpack.dev.js

@@ -24,6 +24,7 @@ module.exports = {
   mode: 'development',
   entry: {
     app: ['./client/index-app.js', 'webpack-hot-middleware/client'],
+    legacy: ['./client/index-legacy.js', 'webpack-hot-middleware/client'],
     setup: ['./client/index-setup.js', 'webpack-hot-middleware/client']
   },
   output: {
@@ -194,14 +195,21 @@ module.exports = {
       filename: '../server/views/master.pug',
       hash: false,
       inject: false,
-      excludeChunks: ['setup']
+      excludeChunks: ['setup', 'legacy']
+    }),
+    new HtmlWebpackPlugin({
+      template: 'dev/templates/legacy.pug',
+      filename: '../server/views/legacy.pug',
+      hash: false,
+      inject: false,
+      excludeChunks: ['setup', 'app']
     }),
     new HtmlWebpackPlugin({
       template: 'dev/templates/setup.pug',
       filename: '../server/views/setup.pug',
       hash: false,
       inject: false,
-      excludeChunks: ['app']
+      excludeChunks: ['app', 'legacy']
     }),
     new HtmlWebpackPugPlugin(),
     new SriWebpackPlugin({

+ 10 - 2
dev/webpack/webpack.prod.js

@@ -27,6 +27,7 @@ module.exports = {
   mode: 'production',
   entry: {
     app: './client/index-app.js',
+    legacy: './client/index-legacy.js',
     setup: './client/index-setup.js'
   },
   output: {
@@ -204,14 +205,21 @@ module.exports = {
       filename: '../server/views/master.pug',
       hash: false,
       inject: false,
-      excludeChunks: ['setup']
+      excludeChunks: ['setup', 'legacy']
+    }),
+    new HtmlWebpackPlugin({
+      template: 'dev/templates/legacy.pug',
+      filename: '../server/views/legacy.pug',
+      hash: false,
+      inject: false,
+      excludeChunks: ['setup', 'app']
     }),
     new HtmlWebpackPlugin({
       template: 'dev/templates/setup.pug',
       filename: '../server/views/setup.pug',
       hash: false,
       inject: false,
-      excludeChunks: ['app']
+      excludeChunks: ['app', 'legacy']
     }),
     new HtmlWebpackPugPlugin(),
     new ScriptExtHtmlWebpackPlugin({

+ 8 - 2
package.json

@@ -180,6 +180,7 @@
     "@babel/polyfill": "^7.4.4",
     "@babel/preset-env": "^7.4.5",
     "@panter/vue-i18next": "0.15.1",
+    "@vue/babel-preset-app": "3.9.2",
     "animate-sass": "0.8.2",
     "animated-number-vue": "0.1.5",
     "apollo-cache-inmemory": "1.6.2",
@@ -197,6 +198,7 @@
     "babel-loader": "^8.0.6",
     "babel-plugin-graphql-tag": "2.4.0",
     "babel-plugin-lodash": "3.3.4",
+    "babel-plugin-prismjs": "1.0.2",
     "babel-plugin-transform-imports": "1.5.1",
     "brace": "0.11.1",
     "cache-loader": "4.0.0",
@@ -243,6 +245,7 @@
     "postcss-loader": "3.0.0",
     "postcss-preset-env": "6.6.0",
     "postcss-selector-parser": "6.0.2",
+    "prismjs": "1.16.0",
     "pug-lint": "2.5.0",
     "pug-loader": "2.4.0",
     "pug-plain-loader": "1.0.0",
@@ -299,9 +302,12 @@
   },
   "browserslist": [
     "> 1%",
-    "last 2 versions",
+    "last 2 major versions",
     "Firefox ESR",
-    "not ie < 11"
+    "not ie > 0",
+    "not ie_mob > 0",
+    "not android > 0",
+    "not dead"
   ],
   "postcss": {
     "plugins": {

+ 9 - 1
server/controllers/common.js

@@ -195,7 +195,15 @@ router.get('/*', async (req, res, next) => {
           head: WIKI.config.theming.injectHead,
           body: WIKI.config.theming.injectBody
         }
-        res.render('page', { page, sidebar, injectCode })
+
+        if (req.query.legacy || req.get('user-agent').indexOf('Trident') >= 0) {
+          if (_.isString(page.toc)) {
+            page.toc = JSON.parse(page.toc)
+          }
+          res.render('legacy', { page, sidebar, injectCode })
+        } else {
+          res.render('page', { page, sidebar, injectCode })
+        }
       } else if (pageArgs.path === 'home') {
         _.set(res.locals, 'pageMeta.title', 'Welcome')
         res.render('welcome')

+ 13 - 0
server/db/migrations-sqlite/2.0.0-beta.217.js

@@ -0,0 +1,13 @@
+exports.up = knex => {
+  return knex.schema
+    .table('locales', table => {
+      table.integer('availability').notNullable().defaultTo(0)
+    })
+}
+
+exports.down = knex => {
+  return knex.schema
+    .table('locales', table => {
+      table.dropColumn('availability')
+    })
+}

+ 13 - 0
server/db/migrations/2.0.0-beta.217.js

@@ -0,0 +1,13 @@
+exports.up = knex => {
+  return knex.schema
+    .table('locales', table => {
+      table.integer('availability').notNullable().defaultTo(0)
+    })
+}
+
+exports.down = knex => {
+  return knex.schema
+    .table('locales', table => {
+      table.dropColumn('availability')
+    })
+}

+ 1 - 1
server/graph/resolvers/localization.js

@@ -13,7 +13,7 @@ module.exports = {
   LocalizationQuery: {
     async locales(obj, args, context, info) {
       let remoteLocales = await WIKI.cache.get('locales')
-      let localLocales = await WIKI.models.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt')
+      let localLocales = await WIKI.models.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'availability')
       remoteLocales = remoteLocales || localLocales
       return _.map(remoteLocales, rl => {
         let isInstalled = _.some(localLocales, ['code', rl.code])

+ 10 - 0
server/graph/resolvers/page.js

@@ -95,6 +95,16 @@ module.exports = {
       } catch (err) {
         return graphHelper.generateError(err)
       }
+    },
+    async migrateToLocale(obj, args, context) {
+      try {
+
+        return {
+          responseResult: graphHelper.generateSuccess('Migrated all content to target locale successfully.')
+        }
+      } catch (err) {
+        return graphHelper.generateError(err)
+      }
     }
   },
   Page: {

+ 1 - 0
server/graph/schemas/localization.graphql

@@ -42,6 +42,7 @@ type LocalizationMutation {
 # -----------------------------------------------
 
 type LocalizationLocale {
+  availability: Int!
   code: String!
   createdAt: Date!
   installDate: Date

+ 2 - 0
server/graph/schemas/page.graphql

@@ -73,6 +73,8 @@ type PageMutation {
   ): DefaultResponse @auth(requires: ["delete:pages", "manage:system"])
 
   flushCache: DefaultResponse @auth(requires: ["manage:system"])
+
+  migrateToLocale: DefaultResponse @auth(requires: ["manage:system"])
 }
 
 # -----------------------------------------------

+ 18 - 9
server/jobs/fetch-graph-locale.js

@@ -28,21 +28,30 @@ module.exports = async (localeCode) => {
     let lcObj = {}
     _.forEach(strings, row => {
       if (_.includes(row.key, '::')) { return }
-      if (_.isEmpty(row.value)) { row.value = row.key }
+      if (_.isEmpty(row.value)) {
+        row.value = row.key
+      }
       _.set(lcObj, row.key.replace(':', '.'), row.value)
     })
 
     const locales = await WIKI.cache.get('locales')
     if (locales) {
       const currentLocale = _.find(locales, ['code', localeCode]) || {}
-      await WIKI.models.locales.query().delete().where('code', localeCode)
-      await WIKI.models.locales.query().insert({
-        code: localeCode,
-        strings: lcObj,
-        isRTL: currentLocale.isRTL,
-        name: currentLocale.name,
-        nativeName: currentLocale.nativeName
-      })
+      const existingLocale = await WIKI.models.locales.query().where('code', localeCode)
+      if (existingLocale) {
+        await WIKI.models.locales.query().patch({
+          strings: lcObj
+        }).where('code', localeCode)
+      } else {
+        await WIKI.models.locales.query().insert({
+          code: localeCode,
+          strings: lcObj,
+          isRTL: currentLocale.isRTL,
+          name: currentLocale.name,
+          nativeName: currentLocale.nativeName,
+          availability: currentLocale.availability
+        })
+      }
     } else {
       throw new Error('Failed to fetch cached locales list! Restart server to resolve this issue.')
     }

+ 6 - 2
server/jobs/sync-graph-locales.js

@@ -17,6 +17,7 @@ module.exports = async () => {
       query: `{
         localization {
           locales {
+            availability
             code
             name
             nativeName
@@ -54,7 +55,9 @@ module.exports = async () => {
         let lcObj = {}
         _.forEach(strings, row => {
           if (_.includes(row.key, '::')) { return }
-          if (_.isEmpty(row.value)) { row.value = row.key }
+          if (_.isEmpty(row.value)) {
+            row.value = row.key
+          }
           _.set(lcObj, row.key.replace(':', '.'), row.value)
         })
 
@@ -63,7 +66,8 @@ module.exports = async () => {
           strings: lcObj,
           isRTL: localeInfo.isRTL,
           name: localeInfo.name,
-          nativeName: localeInfo.nativeName
+          nativeName: localeInfo.nativeName,
+          availability: localeInfo.availability
         }).where('code', currentLocale)
 
         WIKI.logger.info(`Pulled latest locale updates for ${localeInfo.name} from Graph endpoint: [ COMPLETED ]`)

+ 2 - 1
server/models/locales.js

@@ -20,7 +20,8 @@ module.exports = class Locale extends Model {
         name: {type: 'string'},
         nativeName: {type: 'string'},
         createdAt: {type: 'string'},
-        updatedAt: {type: 'string'}
+        updatedAt: {type: 'string'},
+        availability: {type: 'integer'}
       }
     }
   }

+ 339 - 16
yarn.lock

@@ -159,6 +159,18 @@
     "@babel/helper-replace-supers" "^7.4.4"
     "@babel/helper-split-export-declaration" "^7.4.4"
 
+"@babel/helper-create-class-features-plugin@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.0.tgz#02edb97f512d44ba23b3227f1bf2ed43454edac5"
+  integrity sha512-EAoMc3hE5vE5LNhMqDOwB1usHvmRjCDAnH8CD4PVkX9/Yr3W/tcz8xE8QvdZxfsFBDICwZnF2UTHIqslRpvxmA==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-member-expression-to-functions" "^7.0.0"
+    "@babel/helper-optimise-call-expression" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.4.4"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+
 "@babel/helper-define-map@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a"
@@ -384,6 +396,14 @@
     "@babel/helper-remap-async-to-generator" "^7.1.0"
     "@babel/plugin-syntax-async-generators" "^7.2.0"
 
+"@babel/plugin-proposal-class-properties@^7.0.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.0.tgz#5bc6a0537d286fcb4fd4e89975adbca334987007"
+  integrity sha512-9L/JfPCT+kShiiTTzcnBJ8cOwdKVmlC1RcCf9F0F9tERVrM4iWtWnXtjWCRqNm2la2BxO1MPArWNsU9zsSJWSQ==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.5.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
 "@babel/plugin-proposal-class-properties@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz#93a6486eed86d53452ab9bab35e368e9461198ce"
@@ -392,7 +412,7 @@
     "@babel/helper-create-class-features-plugin" "^7.4.4"
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-proposal-decorators@^7.4.4":
+"@babel/plugin-proposal-decorators@^7.1.0", "@babel/plugin-proposal-decorators@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0"
   integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==
@@ -434,6 +454,14 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/plugin-syntax-numeric-separator" "^7.2.0"
 
+"@babel/plugin-proposal-object-rest-spread@^7.3.4":
+  version "7.5.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.4.tgz#250de35d867ce8260a31b1fdac6c4fc1baa99331"
+  integrity sha512-KCx0z3y7y8ipZUMAEEJOyNi11lMb/FOPUjjB113tfowgw0c16EGYos7worCKBcUAh2oG+OBnoUhsnTSoLpV9uA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+
 "@babel/plugin-proposal-object-rest-spread@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005"
@@ -458,7 +486,7 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/plugin-syntax-throw-expressions" "^7.2.0"
 
-"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+"@babel/plugin-proposal-unicode-property-regex@^7.2.0", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
   integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==
@@ -481,7 +509,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-syntax-dynamic-import@^7.2.0":
+"@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.2.0":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612"
   integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==
@@ -516,6 +544,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7"
+  integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
 "@babel/plugin-syntax-numeric-separator@^7.2.0":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.2.0.tgz#7470fe070c2944469a756752a69a6963135018be"
@@ -551,6 +586,15 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-transform-async-to-generator@^7.3.4":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e"
+  integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.1.0"
+
 "@babel/plugin-transform-async-to-generator@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894"
@@ -567,7 +611,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-block-scoping@^7.4.4":
+"@babel/plugin-transform-block-scoping@^7.3.4", "@babel/plugin-transform-block-scoping@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d"
   integrity sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==
@@ -575,7 +619,7 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     lodash "^4.17.11"
 
-"@babel/plugin-transform-classes@^7.4.4":
+"@babel/plugin-transform-classes@^7.3.4", "@babel/plugin-transform-classes@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6"
   integrity sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==
@@ -596,6 +640,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-transform-destructuring@^7.2.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a"
+  integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
 "@babel/plugin-transform-destructuring@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f"
@@ -603,7 +654,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-dotall-regex@^7.4.4":
+"@babel/plugin-transform-dotall-regex@^7.2.0", "@babel/plugin-transform-dotall-regex@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3"
   integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==
@@ -627,14 +678,14 @@
     "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-for-of@^7.4.4":
+"@babel/plugin-transform-for-of@^7.2.0", "@babel/plugin-transform-for-of@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
   integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-function-name@^7.4.4":
+"@babel/plugin-transform-function-name@^7.2.0", "@babel/plugin-transform-function-name@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad"
   integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==
@@ -664,6 +715,16 @@
     "@babel/helper-module-transforms" "^7.1.0"
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-transform-modules-commonjs@^7.2.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74"
+  integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-simple-access" "^7.1.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
 "@babel/plugin-transform-modules-commonjs@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e"
@@ -673,6 +734,15 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/helper-simple-access" "^7.1.0"
 
+"@babel/plugin-transform-modules-systemjs@^7.3.4":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249"
+  integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
 "@babel/plugin-transform-modules-systemjs@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405"
@@ -689,14 +759,14 @@
     "@babel/helper-module-transforms" "^7.1.0"
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
+"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
   version "7.4.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106"
   integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==
   dependencies:
     regexp-tree "^0.1.6"
 
-"@babel/plugin-transform-new-target@^7.4.4":
+"@babel/plugin-transform-new-target@^7.0.0", "@babel/plugin-transform-new-target@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
   integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==
@@ -711,7 +781,7 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/helper-replace-supers" "^7.1.0"
 
-"@babel/plugin-transform-parameters@^7.4.4":
+"@babel/plugin-transform-parameters@^7.2.0", "@babel/plugin-transform-parameters@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
   integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==
@@ -727,7 +797,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-regenerator@^7.4.5":
+"@babel/plugin-transform-regenerator@^7.3.4", "@babel/plugin-transform-regenerator@^7.4.5":
   version "7.4.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f"
   integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==
@@ -741,6 +811,16 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-transform-runtime@^7.4.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.0.tgz#45242c2c9281158c5f06d25beebac63e498a284e"
+  integrity sha512-LmPIZOAgTLl+86gR9KjLXex6P/lRz1fWEjTz6V6QZMmKie51ja3tvzdwORqhHc4RWR8TcZ5pClpRWs0mlaA2ng==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    resolve "^1.8.1"
+    semver "^5.5.1"
+
 "@babel/plugin-transform-shorthand-properties@^7.2.0":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0"
@@ -763,7 +843,7 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/helper-regex" "^7.0.0"
 
-"@babel/plugin-transform-template-literals@^7.4.4":
+"@babel/plugin-transform-template-literals@^7.2.0", "@babel/plugin-transform-template-literals@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
   integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==
@@ -778,7 +858,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
-"@babel/plugin-transform-unicode-regex@^7.4.4":
+"@babel/plugin-transform-unicode-regex@^7.2.0", "@babel/plugin-transform-unicode-regex@^7.4.4":
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f"
   integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==
@@ -795,6 +875,55 @@
     core-js "^2.6.5"
     regenerator-runtime "^0.13.2"
 
+"@babel/preset-env@^7.0.0 < 7.4.0":
+  version "7.3.4"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1"
+  integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-async-generator-functions" "^7.2.0"
+    "@babel/plugin-proposal-json-strings" "^7.2.0"
+    "@babel/plugin-proposal-object-rest-spread" "^7.3.4"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.2.0"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+    "@babel/plugin-syntax-json-strings" "^7.2.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-transform-arrow-functions" "^7.2.0"
+    "@babel/plugin-transform-async-to-generator" "^7.3.4"
+    "@babel/plugin-transform-block-scoped-functions" "^7.2.0"
+    "@babel/plugin-transform-block-scoping" "^7.3.4"
+    "@babel/plugin-transform-classes" "^7.3.4"
+    "@babel/plugin-transform-computed-properties" "^7.2.0"
+    "@babel/plugin-transform-destructuring" "^7.2.0"
+    "@babel/plugin-transform-dotall-regex" "^7.2.0"
+    "@babel/plugin-transform-duplicate-keys" "^7.2.0"
+    "@babel/plugin-transform-exponentiation-operator" "^7.2.0"
+    "@babel/plugin-transform-for-of" "^7.2.0"
+    "@babel/plugin-transform-function-name" "^7.2.0"
+    "@babel/plugin-transform-literals" "^7.2.0"
+    "@babel/plugin-transform-modules-amd" "^7.2.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.2.0"
+    "@babel/plugin-transform-modules-systemjs" "^7.3.4"
+    "@babel/plugin-transform-modules-umd" "^7.2.0"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0"
+    "@babel/plugin-transform-new-target" "^7.0.0"
+    "@babel/plugin-transform-object-super" "^7.2.0"
+    "@babel/plugin-transform-parameters" "^7.2.0"
+    "@babel/plugin-transform-regenerator" "^7.3.4"
+    "@babel/plugin-transform-shorthand-properties" "^7.2.0"
+    "@babel/plugin-transform-spread" "^7.2.0"
+    "@babel/plugin-transform-sticky-regex" "^7.2.0"
+    "@babel/plugin-transform-template-literals" "^7.2.0"
+    "@babel/plugin-transform-typeof-symbol" "^7.2.0"
+    "@babel/plugin-transform-unicode-regex" "^7.2.0"
+    browserslist "^4.3.4"
+    invariant "^2.2.2"
+    js-levenshtein "^1.1.3"
+    semver "^5.3.0"
+
 "@babel/preset-env@^7.4.5":
   version "7.4.5"
   resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58"
@@ -849,6 +978,14 @@
     js-levenshtein "^1.1.3"
     semver "^5.5.0"
 
+"@babel/runtime-corejs2@^7.2.0":
+  version "7.5.4"
+  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.4.tgz#7111dbb344acce1f7dd601786cff40d516b27a96"
+  integrity sha512-sHv74OzyZ18d6tjHU0HmlVES3+l+lydkOMTiKsJSTGWcTBpIMfXLEgduahlJrQjknW9RCQAqLIEdLOHjBmq/hg==
+  dependencies:
+    core-js "^2.6.5"
+    regenerator-runtime "^0.13.2"
+
 "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0":
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a"
@@ -1546,6 +1683,89 @@
   resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
   integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==
 
+"@vue/babel-helper-vue-jsx-merge-props@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040"
+  integrity sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw==
+
+"@vue/babel-plugin-transform-vue-jsx@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.0.0.tgz#ebcbf39c312c94114c8c4f407ee4f6c97aa45432"
+  integrity sha512-U+JNwVQSmaLKjO3lzCUC3cNXxprgezV1N+jOdqbP4xWNaqtWUCJnkjTVcgECM18A/AinDKPcUUeoyhU7yxUxXQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+    "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0"
+    html-tags "^2.0.0"
+    lodash.kebabcase "^4.1.1"
+    svg-tags "^1.0.0"
+
+"@vue/babel-preset-app@3.9.2":
+  version "3.9.2"
+  resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.9.2.tgz#b72a9b06abbe3f8f272783be13951271277be338"
+  integrity sha512-0suuCbu4jkVcVYBjPmuKxeDbrhwThYZHu3DUmtsVuOzFEGeXmco60VmXveniL/bnDUdZyknSuYP4FxgS34gw9w==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/plugin-proposal-class-properties" "^7.0.0"
+    "@babel/plugin-proposal-decorators" "^7.1.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.0.0"
+    "@babel/plugin-transform-runtime" "^7.4.0"
+    "@babel/preset-env" "^7.0.0 < 7.4.0"
+    "@babel/runtime" "^7.0.0"
+    "@babel/runtime-corejs2" "^7.2.0"
+    "@vue/babel-preset-jsx" "^1.0.0"
+    babel-plugin-dynamic-import-node "^2.2.0"
+    babel-plugin-module-resolver "3.2.0"
+    core-js "^2.6.5"
+
+"@vue/babel-preset-jsx@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.0.0.tgz#e515cd453a5a8ea6b0f30b2bb92f266d8ab4e9f5"
+  integrity sha512-5CbDu/QHS+TtQNw5aYAffiMxBBB2Eo9+RJpS8X+6FJbdG5Rvc4TVipEqkrg0pJviWadNg7TEy0Uz4o7VNXeIZw==
+  dependencies:
+    "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0"
+    "@vue/babel-plugin-transform-vue-jsx" "^1.0.0"
+    "@vue/babel-sugar-functional-vue" "^1.0.0"
+    "@vue/babel-sugar-inject-h" "^1.0.0"
+    "@vue/babel-sugar-v-model" "^1.0.0"
+    "@vue/babel-sugar-v-on" "^1.0.0"
+
+"@vue/babel-sugar-functional-vue@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.0.0.tgz#17e2c4ca27b74b244da3b923240ec91d10048cb3"
+  integrity sha512-XE/jNaaorTuhWayCz+QClk5AB9OV5HzrwbzEC6sIUY0J60A28ONQKeTwxfidW42egOkqNH/UU6eE3KLfmiDj0Q==
+  dependencies:
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@vue/babel-sugar-inject-h@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.0.0.tgz#e5efb6c5b5b7988dc03831af6d133bf7bcde6347"
+  integrity sha512-NxWU+DqtbZgfGvd25GPoFMj+rvyQ8ZA1pHj8vIeqRij+vx3sXoKkObjA9ulZunvWw5F6uG9xYy4ytpxab/X+Hg==
+  dependencies:
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@vue/babel-sugar-v-model@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.0.0.tgz#f4da56aa67f65a349bd2c269a95e72e601af4613"
+  integrity sha512-Pfg2Al0io66P1eO6zUbRIgpyKCU2qTnumiE0lao/wA/uNdb7Dx5Tfd1W6tO5SsByETPnEs8i8+gawRIXX40rFw==
+  dependencies:
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+    "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0"
+    "@vue/babel-plugin-transform-vue-jsx" "^1.0.0"
+    camelcase "^5.0.0"
+    html-tags "^2.0.0"
+    svg-tags "^1.0.0"
+
+"@vue/babel-sugar-v-on@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.0.0.tgz#a633ee8fe205763e865b011246981b7f89668033"
+  integrity sha512-2aqJaDLKdSSGlxZU+GjFERaSNUaa6DQreV+V/K4W/6Lxj8520/r1lChWEa/zuAoPD2Vhy0D2QrqqO+I0D6CkKw==
+  dependencies:
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+    "@vue/babel-plugin-transform-vue-jsx" "^1.0.0"
+    camelcase "^5.0.0"
+
 "@vue/component-compiler-utils@^2.5.1":
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.5.2.tgz#a8d57e773354ab10e4742c7d6a8dd86184d4d7be"
@@ -2645,6 +2865,13 @@ babel-loader@^8.0.6:
     mkdirp "^0.5.1"
     pify "^4.0.1"
 
+babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+  integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+  dependencies:
+    object.assign "^4.1.0"
+
 babel-plugin-graphql-tag@2.4.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-graphql-tag/-/babel-plugin-graphql-tag-2.4.0.tgz#22e64f52917cc8dc8b376a7d1db8dec433f8b059"
@@ -2681,6 +2908,22 @@ babel-plugin-lodash@3.3.4:
     lodash "^4.17.10"
     require-package-name "^2.0.1"
 
+babel-plugin-module-resolver@3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7"
+  integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==
+  dependencies:
+    find-babel-config "^1.1.0"
+    glob "^7.1.2"
+    pkg-up "^2.0.0"
+    reselect "^3.0.1"
+    resolve "^1.4.0"
+
+babel-plugin-prismjs@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/babel-plugin-prismjs/-/babel-plugin-prismjs-1.0.2.tgz#837bf6b32168b3ba624c054fc755946deb1b63fa"
+  integrity sha512-WbUE86Aih6h6daLpyavuikEXECrkon21oWh4MOHa5stMfY/IK1e/Sr79qEGhl7KrL16fMChB3tdbVR82ubnzOg==
+
 babel-plugin-transform-imports@1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-imports/-/babel-plugin-transform-imports-1.5.1.tgz#b3756696aea907719d0d63b0e67c88fba963adb0"
@@ -3061,6 +3304,15 @@ browserslist@^4.0.0:
     electron-to-chromium "^1.3.103"
     node-releases "^1.1.3"
 
+browserslist@^4.3.4:
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.4.tgz#fd0638b3f8867fec2c604ed0ed9300379f8ec7c2"
+  integrity sha512-ErJT8qGfRt/VWHSr1HeqZzz50DvxHtr1fVL1m5wf20aGrG8e1ce8fpZ2EjZEfs09DDZYSvtRaDlMpWslBf8Low==
+  dependencies:
+    caniuse-lite "^1.0.30000981"
+    electron-to-chromium "^1.3.188"
+    node-releases "^1.1.25"
+
 browserslist@^4.4.2, browserslist@^4.5.4:
   version "4.5.5"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.5.tgz#fe1a352330d2490d5735574c149a85bc18ef9b82"
@@ -3336,6 +3588,11 @@ caniuse-lite@^1.0.30000975:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000976.tgz#d30fe12662cb2a21e130d307db9907513ca830a2"
   integrity sha512-tleNB1IwPRqZiod6nUNum63xQCMN96BUO2JTeiwuRM7p9d616EHsMBjBWJMudX39qCaPuWY8KEWzMZq7A9XQMQ==
 
+caniuse-lite@^1.0.30000981:
+  version "1.0.30000981"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000981.tgz#5b6828803362363e5a1deba2eb550185cf6cec8f"
+  integrity sha512-JTByHj4DQgL2crHNMK6PibqAMrqqb/Vvh0JrsTJVSWG4VSUrT16EklkuRZofurlMjgA9e+zlCM4Y39F3kootMQ==
+
 capture-exit@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
@@ -3564,7 +3821,7 @@ clipboard@^1.7.1:
     select "^1.1.2"
     tiny-emitter "^2.0.0"
 
-clipboard@^2.0.4:
+clipboard@^2.0.0, clipboard@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d"
   integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==
@@ -5003,6 +5260,11 @@ electron-to-chromium@^1.3.164:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.169.tgz#f9722d3f6a18a19c1fc1216bf9e1654daafab539"
   integrity sha512-CxKt4ONON7m0ekVaFzvTZakHgGQsLMRH0J8W6h4lhyBNgskj3CIJz4bj+bh5+G26ztAe6dZjmYUeEW4u/VSnLQ==
 
+electron-to-chromium@^1.3.188:
+  version "1.3.188"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.188.tgz#e28e1afe4bb229989e280bfd3b395c7ec03c8b7a"
+  integrity sha512-tEQcughYIMj8WDMc59EGEtNxdGgwal/oLLTDw+NEqJRJwGflQvH3aiyiexrWeZOETP4/ko78PVr6gwNhdozvuQ==
+
 elliptic@^6.0.0:
   version "6.4.1"
   resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a"
@@ -5755,6 +6017,14 @@ finalhandler@~1.1.2:
     statuses "~1.5.0"
     unpipe "~1.0.0"
 
+find-babel-config@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2"
+  integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==
+  dependencies:
+    json5 "^0.5.1"
+    path-exists "^3.0.0"
+
 find-cache-dir@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
@@ -6677,6 +6947,11 @@ html-minifier@^3.2.3:
     relateurl "0.2.x"
     uglify-js "3.4.x"
 
+html-tags@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b"
+  integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=
+
 html-webpack-plugin@3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
@@ -7666,7 +7941,7 @@ json5@2.0.0:
   dependencies:
     minimist "^1.2.0"
 
-json5@^0.5.0:
+json5@^0.5.0, json5@^0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
   integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
@@ -9108,6 +9383,13 @@ node-releases@^1.1.23:
   dependencies:
     semver "^5.3.0"
 
+node-releases@^1.1.25:
+  version "1.1.25"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.25.tgz#0c2d7dbc7fed30fbe02a9ee3007b8c90bf0133d3"
+  integrity sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==
+  dependencies:
+    semver "^5.3.0"
+
 node-releases@^1.1.3:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.7.tgz#b09a10394d0ed8f7778f72bb861dde68b146303b"
@@ -9320,6 +9602,16 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
 object.defaults@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf"
@@ -10141,6 +10433,13 @@ pkg-dir@^4.1.0:
   dependencies:
     find-up "^4.0.0"
 
+pkg-up@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+  integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
+  dependencies:
+    find-up "^2.1.0"
+
 pkginfo@0.2.x, pkginfo@^0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.2.3.tgz#7239c42a5ef6c30b8f328439d9b9ff71042490f8"
@@ -11323,6 +11622,13 @@ pretty-error@^2.0.2:
     renderkid "^2.0.1"
     utila "~0.4"
 
+prismjs@1.16.0:
+  version "1.16.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.16.0.tgz#406eb2c8aacb0f5f0f1167930cb83835d10a4308"
+  integrity sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==
+  optionalDependencies:
+    clipboard "^2.0.0"
+
 private@^0.1.6:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -12279,6 +12585,11 @@ require_optional@^1.0.1:
     resolve-from "^2.0.0"
     semver "^5.1.0"
 
+reselect@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
+  integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=
+
 resolve-cwd@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@@ -12344,6 +12655,13 @@ resolve@^1.10.1, resolve@^1.11.0:
   dependencies:
     path-parse "^1.0.6"
 
+resolve@^1.4.0, resolve@^1.8.1:
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
+  integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
+  dependencies:
+    path-parse "^1.0.6"
+
 restore-cursor@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -13361,6 +13679,11 @@ svg-pan-zoom@^3.6.0:
   resolved "https://registry.yarnpkg.com/svg-pan-zoom/-/svg-pan-zoom-3.6.0.tgz#31d8137d7e079e8f059effd85c7605b0567b6e4b"
   integrity sha512-ZBEL2z/n/W2fLLFzn3NTQgd+7QEfbrKvKmu29U3qvMflmJgLaWkwKbOzWJYESFidTiGYMHkijAKmY6m64moyYg==
 
+svg-tags@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
+  integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
+
 svgo@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985"