瀏覽代碼

HUE-9266 [ui] Switch the hive query plan component from ko to a vue web component

Johan Ahlen 5 年之前
父節點
當前提交
65adceb01b

+ 7 - 2
.babelrc

@@ -8,8 +8,13 @@
       "root": ["./desktop/core/src/desktop/js"]
     }],
     "@babel/plugin-syntax-dynamic-import",
-    "@babel/proposal-class-properties",
-    "@babel/proposal-object-rest-spread"
+    ["@babel/plugin-proposal-decorators", {
+      "legacy": true
+    }],
+    ["@babel/proposal-class-properties", {
+      "loose": true
+    }],
+    "@babel/proposal-object-rest-spread",
   ],
   "overrides": [{
     "test": "./desktop/core/src/desktop/js/parse/sqlAutocompleteParser.js",

+ 2 - 6
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -58,6 +58,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 <link rel="stylesheet" href="${ static('desktop/css/bootstrap-spinedit.css') }">
 <link rel="stylesheet" href="${ static('desktop/css/bootstrap-slider.css') }">
 
+${ render_bundle('vendors~jobBrowser') | n,unicode }
 ${ render_bundle('jobBrowser') | n,unicode }
 
 <script src="${ static('desktop/ext/js/bootstrap-datepicker.min.js') }" type="text/javascript" charset="utf-8"></script>
@@ -1807,12 +1808,7 @@ ${ render_bundle('jobBrowser') | n,unicode }
         <!-- ko if: $root.job().mainType() == 'queries-hive' -->
         <div class="tab-pane active" id="queries-page-hive-plan-text${ SUFFIX }" data-profile="plan">
           <!-- ko if: properties.plan && properties.plan().plan -->
-            <!-- ko component: {
-              name: 'hive-query-plan',
-              params: {
-                plan: properties.plan().plan
-              }
-            } --><!-- /ko -->
+            <hive-query-plan></hive-query-plan>
           <!-- /ko -->
         </div>
         <div class="tab-pane" id="queries-page-hive-stmt${ SUFFIX }" data-profile="stmt">

+ 1 - 2
desktop/core/src/desktop/js/apps/jobBrowser/app.js

@@ -14,9 +14,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import './components/ko.hiveQueryPlan';
-
 import huePubSub from 'utils/huePubSub';
+import './components/hiveQueryPlan.vue';
 
 huePubSub.subscribe('app.dom.loaded', app => {
   if (app !== 'jobbrowser') {

+ 0 - 3
desktop/core/src/desktop/js/apps/jobBrowser/components/__snapshots__/ko.hiveQueryPlan.test.js.snap

@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ko.hiveQueryPlan.js should render component 1`] = `"<div data-bind=\\"descendantsComplete: descendantsComplete, component: { name: &quot;hive-query-plan&quot;, params: params }\\"><pre data-bind=\\"text: plan\\">foo</pre></div>"`;

+ 18 - 0
desktop/core/src/desktop/js/apps/jobBrowser/components/hiveQueryPlan.vue

@@ -0,0 +1,18 @@
+<template>
+  <div>{{title}}</div>
+</template>
+
+<script>
+  import Vue from 'vue';
+  import Component from 'vue-class-component'
+  import wrap from '@vue/web-component-wrapper'
+
+  export const HIVE_QUERY_PLAN_COMPONENT = 'hive-query-plan';
+
+  @Component
+  export default class HiveQueryPlan extends Vue {
+    title = 'Web Components FTW.';
+  }
+
+  window.customElements.define(HIVE_QUERY_PLAN_COMPONENT, wrap(Vue, HiveQueryPlan))
+</script>

+ 0 - 34
desktop/core/src/desktop/js/apps/jobBrowser/components/ko.hiveQueryPlan.js

@@ -1,34 +0,0 @@
-// Licensed to Cloudera, Inc. under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  Cloudera, Inc. licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import componentUtils from 'ko/components/componentUtils';
-import DisposableComponent from 'ko/components/DisposableComponent';
-
-export const HIVE_QUERY_PLAN_COMPONENT = 'hive-query-plan';
-
-// prettier-ignore
-const TEMPLATE = `
-  <pre data-bind="text: plan"></pre>
-`;
-
-class HiveQueryPlan extends DisposableComponent {
-  constructor(params) {
-    super();
-    this.plan = params.plan;
-  }
-}
-
-componentUtils.registerComponent(HIVE_QUERY_PLAN_COMPONENT, HiveQueryPlan, TEMPLATE);

+ 0 - 30
desktop/core/src/desktop/js/apps/jobBrowser/components/ko.hiveQueryPlan.test.js

@@ -1,30 +0,0 @@
-// Licensed to Cloudera, Inc. under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  Cloudera, Inc. licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { koSetup } from 'jest/koTestUtils';
-import { HIVE_QUERY_PLAN_COMPONENT } from './ko.hiveQueryPlan';
-
-describe('ko.hiveQueryPlan.js', () => {
-  const setup = koSetup();
-
-  it('should render component', async () => {
-    const element = await setup.renderComponent(HIVE_QUERY_PLAN_COMPONENT, {
-      plan: 'foo'
-    });
-
-    expect(element.innerHTML).toMatchSnapshot();
-  });
-});

+ 1 - 2
desktop/core/src/desktop/templates/hue.mako

@@ -277,12 +277,11 @@ ${ hueIcons.symbols() }
 </div>
 ${ commonshare() | n,unicode }
 
-${ render_bundle('vendors~hue~jobBrowser~notebook~tableBrowser') | n,unicode }
 ${ render_bundle('vendors~hue~notebook~tableBrowser') | n,unicode }
 ${ render_bundle('vendors~hue~notebook') | n,unicode }
 ${ render_bundle('vendors~hue') | n,unicode }
-${ render_bundle('hue~notebook') | n,unicode }
 ${ render_bundle('hue~notebook~tableBrowser') | n,unicode }
+${ render_bundle('hue~notebook') | n,unicode }
 ${ render_bundle('hue~tableBrowser') | n,unicode }
 ${ render_bundle('hue') | n,unicode }
 

+ 205 - 3
package-lock.json

@@ -1765,6 +1765,12 @@
         "@babel/types": "^7.7.4"
       }
     },
+    "@babel/helper-validator-identifier": {
+      "version": "7.10.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
+      "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==",
+      "dev": true
+    },
     "@babel/helper-wrap-function": {
       "version": "7.10.1",
       "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz",
@@ -2282,6 +2288,175 @@
         }
       }
     },
+    "@babel/plugin-proposal-decorators": {
+      "version": "7.10.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.3.tgz",
+      "integrity": "sha512-Rzwn5tcYFTdWWK3IrhMZkMDjzFQLIGYqHvv9XuzNnEB91Y6gHr/JjazYV1Yec9g0yMLhy1p/21eiW1P7f5UN4A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.10.3",
+        "@babel/helper-plugin-utils": "^7.10.3",
+        "@babel/plugin-syntax-decorators": "^7.10.1"
+      },
+      "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz",
+          "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.3"
+          }
+        },
+        "@babel/helper-create-class-features-plugin": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.3.tgz",
+          "integrity": "sha512-iRT9VwqtdFmv7UheJWthGc/h2s7MqoweBF9RUj77NFZsg9VfISvBTum3k6coAhJ8RWv2tj3yUjA03HxPd0vfpQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-function-name": "^7.10.3",
+            "@babel/helper-member-expression-to-functions": "^7.10.3",
+            "@babel/helper-optimise-call-expression": "^7.10.3",
+            "@babel/helper-plugin-utils": "^7.10.3",
+            "@babel/helper-replace-supers": "^7.10.1",
+            "@babel/helper-split-export-declaration": "^7.10.1"
+          }
+        },
+        "@babel/helper-function-name": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.3.tgz",
+          "integrity": "sha512-FvSj2aiOd8zbeqijjgqdMDSyxsGHaMt5Tr0XjQsGKHD3/1FP3wksjnLAWzxw7lvXiej8W1Jt47SKTZ6upQNiRw==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-get-function-arity": "^7.10.3",
+            "@babel/template": "^7.10.3",
+            "@babel/types": "^7.10.3"
+          }
+        },
+        "@babel/helper-get-function-arity": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.3.tgz",
+          "integrity": "sha512-iUD/gFsR+M6uiy69JA6fzM5seno8oE85IYZdbVVEuQaZlEzMO2MXblh+KSPJgsZAUx0EEbWXU0yJaW7C9CdAVg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.3"
+          }
+        },
+        "@babel/helper-member-expression-to-functions": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.3.tgz",
+          "integrity": "sha512-q7+37c4EPLSjNb2NmWOjNwj0+BOyYlssuQ58kHEWk1Z78K5i8vTUsteq78HMieRPQSl/NtpQyJfdjt3qZ5V2vw==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.3"
+          }
+        },
+        "@babel/helper-optimise-call-expression": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.3.tgz",
+          "integrity": "sha512-kT2R3VBH/cnSz+yChKpaKRJQJWxdGoc6SjioRId2wkeV3bK0wLLioFpJROrX0U4xr/NmxSSAWT/9Ih5snwIIzg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.3"
+          }
+        },
+        "@babel/helper-plugin-utils": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.3.tgz",
+          "integrity": "sha512-j/+j8NAWUTxOtx4LKHybpSClxHoq6I91DQ/mKgAXn5oNUPIUiGppjPIX3TDtJWPrdfP9Kfl7e4fgVMiQR9VE/g==",
+          "dev": true
+        },
+        "@babel/helper-split-export-declaration": {
+          "version": "7.10.1",
+          "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz",
+          "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.1"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz",
+          "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.3",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.3.tgz",
+          "integrity": "sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.3.tgz",
+          "integrity": "sha512-5BjI4gdtD+9fHZUsaxPHPNpwa+xRkDO7c7JbhYn2afvrkDu5SfAAbi9AIMXw2xEhO/BR35TqiW97IqNvCo/GqA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.3",
+            "@babel/parser": "^7.10.3",
+            "@babel/types": "^7.10.3"
+          }
+        },
+        "@babel/types": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.3.tgz",
+          "integrity": "sha512-nZxaJhBXBQ8HVoIcGsf9qWep3Oh3jCENK54V4mRF7qaJabVsAYdbTtmSD8WmAp1R6ytPiu5apMwSXyxB1WlaBA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.3",
+            "lodash": "^4.17.13",
+            "to-fast-properties": "^2.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "js-tokens": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+          "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
+        "to-fast-properties": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+          "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+          "dev": true
+        }
+      }
+    },
     "@babel/plugin-proposal-dynamic-import": {
       "version": "7.10.1",
       "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz",
@@ -2547,6 +2722,23 @@
         }
       }
     },
+    "@babel/plugin-syntax-decorators": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.1.tgz",
+      "integrity": "sha512-a9OAbQhKOwSle1Vr0NJu/ISg1sPfdEkfRKWpgPuzhnWWzForou2gIeUIIwjAMHRekhhpJ7eulZlYs0H14Cbi+g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.1"
+      },
+      "dependencies": {
+        "@babel/helper-plugin-utils": {
+          "version": "7.10.3",
+          "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.3.tgz",
+          "integrity": "sha512-j/+j8NAWUTxOtx4LKHybpSClxHoq6I91DQ/mKgAXn5oNUPIUiGppjPIX3TDtJWPrdfP9Kfl7e4fgVMiQR9VE/g==",
+          "dev": true
+        }
+      }
+    },
     "@babel/plugin-syntax-dynamic-import": {
       "version": "7.8.3",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -5282,6 +5474,11 @@
         }
       }
     },
+    "@vue/web-component-wrapper": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz",
+      "integrity": "sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw=="
+    },
     "@webassemblyjs/ast": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@@ -20072,6 +20269,11 @@
       "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
       "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
     },
+    "vue-class-component": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.3.tgz",
+      "integrity": "sha512-oEqYpXKaFN+TaXU+mRLEx8dX0ah85aAJEe61mpdoUrq0Bhe/6sWhyZX1JjMQLhVsHAkncyhedhmCdDVSasUtDw=="
+    },
     "vue-hot-reload-api": {
       "version": "2.3.4",
       "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -20079,9 +20281,9 @@
       "dev": true
     },
     "vue-loader": {
-      "version": "15.9.1",
-      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.1.tgz",
-      "integrity": "sha512-IaPU2KOPjs/QjMlxFs/TiTtQUSbftQ7lsAvoxe21rtcQohsMhx+1AltXCNhZIpIn46PtODiAgz+o8RbMpKtmJw==",
+      "version": "15.9.3",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz",
+      "integrity": "sha512-Y67VnGGgVLH5Voostx8JBZgPQTlDQeOVBLOEsjc2cXbCYBKexSKEpOA56x0YZofoDOTszrLnIShyOX1p9uCEHA==",
       "dev": true,
       "requires": {
         "@vue/component-compiler-utils": "^3.1.0",

+ 7 - 4
package.json

@@ -28,6 +28,7 @@
     }
   },
   "dependencies": {
+    "@vue/web-component-wrapper": "^1.2.0",
     "clipboard": "1.7.1",
     "core-js": "^3.4.2",
     "d3": "5.7.0",
@@ -35,11 +36,11 @@
     "dropzone": "5.5.1",
     "filesize": "4.0.0",
     "jquery": "3.5.0",
+    "jquery-contextmenu": "^2.9.0",
     "jquery-form": "4.2.2",
     "jquery-mousewheel": "3.1.13",
     "jquery-ui": "1.12.1",
     "jquery.cookie": "1.4.1",
-    "jquery-contextmenu": "^2.9.0",
     "knockout": "3.5.1",
     "knockout-sortable": "1.2.0",
     "knockout-switch-case": "2.1.0",
@@ -57,13 +58,15 @@
     "selectize": "0.12.6",
     "selectize-plugin-clear": "0.0.3",
     "sprintf-js": "1.1.2",
-    "vue": "^2.6.11",
-    "vue-template-compiler": "^2.6.11"
+    "vue": "2.6.11",
+    "vue-class-component": "7.2.3",
+    "vue-template-compiler": "2.6.11"
   },
   "devDependencies": {
     "@babel/core": "7.10.2",
     "@babel/node": "7.10.1",
     "@babel/plugin-proposal-class-properties": "7.10.1",
+    "@babel/plugin-proposal-decorators": "^7.10.3",
     "@babel/plugin-proposal-object-rest-spread": "7.10.1",
     "@babel/plugin-syntax-dynamic-import": "7.8.3",
     "@babel/preset-env": "7.10.2",
@@ -99,7 +102,7 @@
     "stylelint-config-standard": "20.0.0",
     "ts-loader": "7.0.5",
     "typescript": "3.9.5",
-    "vue-loader": "^15.9.1",
+    "vue-loader": "15.9.3",
     "webpack": "4.43.0",
     "webpack-bundle-analyzer": "3.8.0",
     "webpack-bundle-tracker": "0.4.3",