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

feat(ui-devkit): Create ui-devkit package for developing UI extensions

This package exposes an API which allows embedded UI extension applications to access core functionality. So far implements GraphQL queries & mutations. Relates to #225
Michael Bromley 6 лет назад
Родитель
Сommit
20cd34d402

+ 1 - 1
packages/admin-ui/package.json

@@ -36,7 +36,7 @@
     "@ng-select/ng-select": "^3.0.5",
     "@ngx-translate/core": "^11.0.1",
     "@ngx-translate/http-loader": "^4.0.0",
-    "@vendure/ui-extension-devkit": "^0.6.4",
+    "@vendure/ui-devkit": "^0.6.4",
     "@webcomponents/custom-elements": "^1.2.4",
     "apollo-angular": "^1.6.0",
     "apollo-cache-inmemory": "^1.6.2",

+ 1 - 1
packages/admin-ui/src/app/shared/components/extension-host/extension-host.component.ts

@@ -9,7 +9,7 @@ import { ExtensionHostService } from './extension-host.service';
     selector: 'vdr-extension-host',
     templateUrl: './extension-host.component.html',
     styleUrls: ['./extension-host.component.scss'],
-    changeDetection: ChangeDetectionStrategy.OnPush,
+    changeDetection: ChangeDetectionStrategy.Default,
     providers: [ExtensionHostService],
 })
 export class ExtensionHostComponent implements OnInit {

+ 42 - 10
packages/admin-ui/src/app/shared/components/extension-host/extension-host.service.ts

@@ -1,51 +1,83 @@
 import { Injectable, OnDestroy } from '@angular/core';
-import { DataService } from '@vendure/admin-ui/src/app/data/providers/data.service';
+import { ExtensionMesssage, MessageResponse } from '@vendure/common/lib/extension-host-types';
 import { parse } from 'graphql';
+import { merge, Observer, Subject } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+import { assertNever } from 'shared/shared-utils';
 
-import { ExtensionMesssage } from './extension-message-types';
+import { DataService } from '../../../data/providers/data.service';
 
 @Injectable()
 export class ExtensionHostService implements OnDestroy {
     private extensionWindow: Window;
+    private cancellationMessage$ = new Subject<string>();
+    private destroyMessage$ = new Subject<void>();
 
     constructor(private dataService: DataService) {}
 
     init(extensionWindow: Window) {
         this.extensionWindow = extensionWindow;
-
         window.addEventListener('message', this.handleMessage);
     }
 
     ngOnDestroy(): void {
         window.removeEventListener('message', this.handleMessage);
+        this.destroyMessage$.next();
     }
 
     private handleMessage = (message: MessageEvent) => {
         const { data, origin } = message;
         if (this.isExtensionMessage(data)) {
+            const cancellation$ = this.cancellationMessage$.pipe(
+                filter(requestId => requestId === data.requestId),
+            );
+            const end$ = merge(cancellation$, this.destroyMessage$);
             switch (data.type) {
-                case 'query': {
+                case 'cancellation': {
+                    this.cancellationMessage$.next(data.requestId);
+                    break;
+                }
+                case 'destroy': {
+                    this.destroyMessage$.next();
+                    break;
+                }
+                case 'graphql-query': {
                     const { document, variables, fetchPolicy } = data.data;
                     this.dataService
                         .query(parse(document), variables, fetchPolicy)
-                        .single$.subscribe(result => this.sendMessage(result, origin, data.requestId));
+                        .stream$.pipe(takeUntil(end$))
+                        .subscribe(this.createObserver(data.requestId, origin));
                     break;
                 }
-                case 'mutation': {
+                case 'graphql-mutation': {
                     const { document, variables } = data.data;
                     this.dataService
                         .mutate(parse(document), variables)
-                        .subscribe(result => this.sendMessage(result, origin, data.requestId));
+                        .pipe(takeUntil(end$))
+                        .subscribe(this.createObserver(data.requestId, origin));
+                    break;
                 }
+                default:
+                    assertNever(data);
             }
         }
     };
 
-    private sendMessage(message: any, origin, requestId: string) {
-        this.extensionWindow.postMessage({ requestId, data: message }, origin);
+    private createObserver(requestId: string, origin: string): Observer<any> {
+        return {
+            next: data => this.sendMessage({ data, error: false, complete: false, requestId }, origin),
+            error: err => this.sendMessage({ data: err, error: true, complete: false, requestId }, origin),
+            complete: () => this.sendMessage({ data: null, error: false, complete: true, requestId }, origin),
+        };
+    }
+
+    private sendMessage(response: MessageResponse, origin: string) {
+        this.extensionWindow.postMessage(response, origin);
     }
 
     private isExtensionMessage(input: any): input is ExtensionMesssage {
-        return input.hasOwnProperty('type') && input.hasOwnProperty('data');
+        return (
+            input.hasOwnProperty('type') && input.hasOwnProperty('data') && input.hasOwnProperty('requestId')
+        );
     }
 }

+ 0 - 26
packages/admin-ui/src/app/shared/components/extension-host/extension-message-types.ts

@@ -1,26 +0,0 @@
-import { WatchQueryFetchPolicy } from 'apollo-client';
-
-export interface BaseExtensionMessage {
-    requestId: string;
-    type: string;
-    data: any;
-}
-
-export interface QueryMessage extends BaseExtensionMessage {
-    type: 'query';
-    data: {
-        document: string;
-        variables?: { [key: string]: any };
-        fetchPolicy?: WatchQueryFetchPolicy;
-    };
-}
-
-export interface MutationMessage extends BaseExtensionMessage {
-    type: 'mutation';
-    data: {
-        document: string;
-        variables?: { [key: string]: any };
-    };
-}
-
-export type ExtensionMesssage = QueryMessage | MutationMessage;

+ 9 - 4
packages/admin-ui/src/compiler/common.ts

@@ -39,10 +39,15 @@ export function copyExtensionModules(extensions: Array<Required<AdminUiExtension
             }
         }
     }
-    fs.copySync(
-        require.resolve('@vendure/ui-extension-devkit'),
-        path.join(STATIC_ASSETS_OUTPUT_DIR, 'ui-extension-devkit.js'),
-    );
+}
+
+/**
+ * Copy the @vendure/ui-devkit files to the static assets dir.
+ */
+export function copyUiDevkit() {
+    const devkitDir = path.join(STATIC_ASSETS_OUTPUT_DIR, 'devkit');
+    fs.ensureDirSync(devkitDir);
+    fs.copySync(require.resolve('@vendure/ui-devkit'), path.join(devkitDir, 'ui-devkit.js'));
 }
 
 /**

+ 2 - 0
packages/admin-ui/src/compiler/compile.ts

@@ -4,6 +4,7 @@ import * as path from 'path';
 
 import {
     copyExtensionModules,
+    copyUiDevkit,
     createExtensionsModules,
     deleteExistingExtensionModules,
     isInVendureMonorepo,
@@ -20,6 +21,7 @@ export function compileAdminUiApp(outputPath: string, extensions: Array<Required
         restoreOriginalExtensionsModule();
         deleteExistingExtensionModules();
         copyExtensionModules(extensions);
+        copyUiDevkit();
         createExtensionsModules(extensions);
 
         const config = isInVendureMonorepo() ? 'plugin-dev' : 'plugin';

+ 11 - 0
packages/admin-ui/src/compiler/watch.ts

@@ -7,6 +7,7 @@ import * as path from 'path';
 import {
     copyExtensionModules,
     copyStaticAsset,
+    copyUiDevkit,
     createExtensionsModules,
     deleteExistingExtensionModules,
     getModuleOutputDir,
@@ -29,6 +30,7 @@ export function watchAdminUiApp(extensions: Array<Required<AdminUiExtension>>, p
     restoreExtensionsModules();
     deleteExistingExtensionModules();
     copyExtensionModules(extensions);
+    copyUiDevkit();
     createExtensionsModules(extensions);
 
     const config = isInVendureMonorepo() ? 'plugin-dev' : 'plugin';
@@ -37,6 +39,7 @@ export function watchAdminUiApp(extensions: Array<Required<AdminUiExtension>>, p
         shell: true,
         stdio: 'inherit',
     });
+    const devkitPath = require.resolve('@vendure/ui-devkit');
 
     let watcher: FSWatcher | undefined;
     for (const extension of extensions) {
@@ -50,6 +53,11 @@ export function watchAdminUiApp(extensions: Array<Required<AdminUiExtension>>, p
         }
     }
 
+    if (watcher) {
+        // watch the ui-devkit package files too
+        watcher.add(devkitPath);
+    }
+
     if (watcher) {
         watcher.on('change', filePath => {
             const extension = extensions.find(e => filePath.includes(e.extensionPath));
@@ -67,6 +75,9 @@ export function watchAdminUiApp(extensions: Array<Required<AdminUiExtension>>, p
                 const dest = path.join(outputDir, filePart);
                 fs.copyFile(filePath, dest);
             }
+            if (filePath.includes(devkitPath)) {
+                copyUiDevkit();
+            }
         });
     }
 

+ 44 - 0
packages/common/src/extension-host-types.ts

@@ -0,0 +1,44 @@
+export type FetchPolicy = 'cache-first' | 'network-only' | 'cache-only' | 'no-cache' | 'standby';
+export type WatchQueryFetchPolicy = FetchPolicy | 'cache-and-network';
+
+export interface BaseExtensionMessage {
+    requestId: string;
+    type: string;
+    data: any;
+}
+
+export interface QueryMessage extends BaseExtensionMessage {
+    type: 'graphql-query';
+    data: {
+        document: string;
+        variables?: { [key: string]: any };
+        fetchPolicy?: WatchQueryFetchPolicy;
+    };
+}
+
+export interface MutationMessage extends BaseExtensionMessage {
+    type: 'graphql-mutation';
+    data: {
+        document: string;
+        variables?: { [key: string]: any };
+    };
+}
+
+export interface CancellationMessage extends BaseExtensionMessage {
+    type: 'cancellation';
+    data: null;
+}
+
+export interface DestroyMessage extends BaseExtensionMessage {
+    type: 'destroy';
+    data: null;
+}
+
+export type ExtensionMesssage = QueryMessage | MutationMessage | CancellationMessage | DestroyMessage;
+
+export interface MessageResponse {
+    requestId: string;
+    data: any;
+    complete: boolean;
+    error: boolean;
+}

+ 2 - 0
packages/ui-devkit/.gitignore

@@ -0,0 +1,2 @@
+lib
+node_modules

+ 5 - 0
packages/ui-devkit/README.md

@@ -0,0 +1,5 @@
+# @vendure/ui-devkit
+
+This package contains utilities for creating extensions to the Vendure Admin UI.
+
+

+ 47 - 0
packages/ui-devkit/package.json

@@ -0,0 +1,47 @@
+{
+  "name": "@vendure/ui-devkit",
+  "version": "0.6.4",
+  "description": "A library for authoring Vendure Admin UI extensions",
+  "keywords": [
+    "vendure",
+    "javascript",
+    "extensions"
+  ],
+  "author": "Michael Bromley <michael@michaelbromley.co.uk>",
+  "homepage": "https://github.com/vendure-ecommerce/vendure#readme",
+  "license": "MIT",
+  "directories": {
+    "lib": "lib"
+  },
+  "files": [
+    "lib"
+  ],
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vendure-ecommerce/vendure.git"
+  },
+  "scripts": {
+    "build": "rimraf ./lib && rollup -c rollup.config.js --configProduction",
+    "watch": "rimraf ./lib && rollup -c rollup.config.js -w",
+    "lint": "tslint --fix --project ./"
+  },
+  "bugs": {
+    "url": "https://github.com/vendure-ecommerce/vendure/issues"
+  },
+  "dependencies": {
+    "@vendure/common": "^0.6.4",
+    "rxjs": "^6.5.3"
+  },
+  "devDependencies": {
+    "@rollup/plugin-node-resolve": "^6.0.0",
+    "@vendure/core": "^0.6.4",
+    "rimraf": "^3.0.0",
+    "rollup": "^1.27.9",
+    "rollup-plugin-terser": "^5.1.2",
+    "rollup-plugin-typescript2": "^0.25.3",
+    "tslib": "^1.10.0",
+    "typescript": "^3.6.4"
+  }
+}

+ 21 - 0
packages/ui-devkit/rollup.config.js

@@ -0,0 +1,21 @@
+// rollup.config.js
+import typescript from 'rollup-plugin-typescript2';
+import { terser } from 'rollup-plugin-terser';
+import resolve from '@rollup/plugin-node-resolve';
+
+export default commandLineArgs => {
+    const isProd = commandLineArgs.configProduction === true;
+    return {
+        input: 'src/index.ts',
+        output: {
+            dir: 'lib',
+            format: 'umd',
+            name: 'VendureUiDevkit',
+        },
+        plugins: [resolve(), typescript(), ...(isProd ? [terser({
+            output: {
+                comments: false,
+            }
+        })] : [])],
+    };
+};

+ 103 - 0
packages/ui-devkit/src/devkit-api.ts

@@ -0,0 +1,103 @@
+import {
+    BaseExtensionMessage,
+    ExtensionMesssage,
+    MessageResponse,
+    WatchQueryFetchPolicy,
+} from '@vendure/common/lib/extension-host-types';
+import { Observable } from 'rxjs';
+import { take } from 'rxjs/operators';
+
+let targetOrigin = 'http://localhost:3000';
+
+/**
+ * Set the [window.postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
+ * `targetOrigin`. The Vendure ui-devkit uses the postMessage API to
+ * enable cross-frame and cross-origin communication between the ui extension code and the Admin UI
+ * app. The `targetOrigin` is a security feature intended to provide control over where messages are sent.
+ */
+export function setTargetOrigin(value: string) {
+    targetOrigin = value;
+}
+
+/**
+ * Perform a GraphQL query and returns either an Observable or a Promise of the result.
+ */
+export function graphQlQuery<T, V extends { [key: string]: any }>(
+    document: string,
+    variables?: { [key: string]: any },
+    fetchPolicy?: WatchQueryFetchPolicy,
+): {
+    then: Promise<T>['then'];
+    stream: Observable<T>;
+} {
+    const result$ = sendMessage('graphql-query', { document, variables, fetchPolicy });
+    return {
+        then: (...args: any[]) =>
+            result$
+                .pipe(take(1))
+                .toPromise()
+                .then(...args),
+        stream: result$,
+    };
+}
+
+/**
+ * Perform a GraphQL mutation and returns either an Observable or a Promise of the result.
+ */
+export function graphQlMutation<T, V extends { [key: string]: any }>(
+    document: string,
+    variables?: { [key: string]: any },
+): {
+    then: Promise<T>['then'];
+    stream: Observable<T>;
+} {
+    const result$ = sendMessage('graphql-mutation', { document, variables });
+    return {
+        then: (...args: any[]) =>
+            result$
+                .pipe(take(1))
+                .toPromise()
+                .then(...args),
+        stream: result$,
+    };
+}
+
+function sendMessage<T extends ExtensionMesssage>(type: T['type'], data: T['data']): Observable<any> {
+    const requestId =
+        type +
+        '__' +
+        Math.random()
+            .toString(36)
+            .substr(3);
+    const message: BaseExtensionMessage = {
+        requestId,
+        type,
+        data,
+    };
+
+    return new Observable<any>(subscriber => {
+        const handleReply = (event: MessageEvent) => {
+            const response: MessageResponse = event.data;
+            if (response && response.requestId === requestId) {
+                if (response.complete) {
+                    subscriber.complete();
+                    tearDown();
+                    return;
+                }
+                if (response.error) {
+                    subscriber.error(response.data);
+                    tearDown();
+                    return;
+                }
+                subscriber.next(response.data);
+            }
+        };
+        const tearDown = () => {
+            window.parent.postMessage({ requestId, type: 'cancellation', data: null }, targetOrigin);
+        };
+        window.addEventListener('message', handleReply);
+        window.parent.postMessage(message, targetOrigin);
+
+        return tearDown;
+    });
+}

+ 1 - 0
packages/ui-devkit/src/index.ts

@@ -0,0 +1 @@
+export * from './devkit-api';

+ 24 - 0
packages/ui-devkit/tsconfig.json

@@ -0,0 +1,24 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "baseUrl": "./",
+    "importHelpers": true,
+    "outDir": "./lib",
+    "sourceMap": true,
+    "declaration": true,
+    "moduleResolution": "node",
+    "strict": true,
+    "noImplicitAny": true,
+    "strictPropertyInitialization": false,
+    "target": "es5",
+    "module": "esnext",
+    "lib": [
+      "es2017",
+      "dom",
+      "esnext.asynciterable"
+    ]
+  },
+  "files": [
+    "./src/index.ts"
+  ]
+}

+ 1 - 0
scripts/changelogs/generate-changelog.ts

@@ -25,6 +25,7 @@ const VALID_SCOPES: string[] = [
     'email-plugin',
     'email',
     'testing',
+    'ui-devkit',
 ];
 
 const mainTemplate = fs.readFileSync(path.join(__dirname, 'template.hbs'), 'utf-8');

+ 139 - 12
yarn.lock

@@ -1854,6 +1854,24 @@
   resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
   integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
 
+"@rollup/plugin-node-resolve@^6.0.0":
+  version "6.0.0"
+  resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-6.0.0.tgz#f351e29b45c007c17df4e28a0efd0d1f5662f59b"
+  integrity sha512-GqWz1CfXOsqpeVMcoM315+O7zMxpRsmhWyhJoxLFHVSp9S64/u02i7len/FnbTNbmgYs+sZyilasijH8UiuboQ==
+  dependencies:
+    "@rollup/pluginutils" "^3.0.0"
+    "@types/resolve" "0.0.8"
+    builtin-modules "^3.1.0"
+    is-module "^1.0.0"
+    resolve "^1.11.1"
+
+"@rollup/pluginutils@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.0.tgz#9978c9d4cd5d6908755362336f699633a3cee343"
+  integrity sha512-qBbGQQaUUiId/lBU9VMeYlVLOoRNvz1fV8HWY5tiGDpI2gdPZHbmOfCjzSdXPhdq3XOfyWvXEBlIPbnM3+9ogQ==
+  dependencies:
+    estree-walker "^0.6.1"
+
 "@samverschueren/stream-to-observable@^0.3.0":
   version "0.3.0"
   resolved "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@@ -2011,6 +2029,11 @@
   resolved "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.1.0.tgz#07075d264e2e5a432624b1e7ffc11379fe66be8a"
   integrity sha1-BwddJk4uWkMmJLHn/8ETef5mvoo=
 
+"@types/estree@*":
+  version "0.0.40"
+  resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.40.tgz#0e6cb9b9bbd098031fa19e4b4e8131bc70e5de13"
+  integrity sha512-p3KZgMto/JyxosKGmnLDJ/dG5wf+qTRMUjHJcspC2oQKa4jP7mz+tv0ND56lLBu3ojHlhzY33Ol+khLyNmilkA==
+
 "@types/events@*":
   version "3.0.0"
   resolved "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
@@ -2345,6 +2368,13 @@
   resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
   integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
 
+"@types/resolve@0.0.8":
+  version "0.0.8"
+  resolved "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
+  integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
+  dependencies:
+    "@types/node" "*"
+
 "@types/selenium-webdriver@^3.0.0":
   version "3.0.16"
   resolved "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz#50a4755f8e33edacd9c406729e9b930d2451902a"
@@ -2690,6 +2720,11 @@ acorn@^6.0.1, acorn@^6.2.0:
   resolved "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
   integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
 
+acorn@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c"
+  integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==
+
 address@^1.0.1:
   version "1.1.2"
   resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
@@ -4006,6 +4041,11 @@ builtin-modules@^1.1.1:
   resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
   integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
 
+builtin-modules@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
+  integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==
+
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -6177,6 +6217,11 @@ estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
   resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
+estree-walker@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
+  integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
+
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -6603,6 +6648,15 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
     make-dir "^2.0.0"
     pkg-dir "^3.0.0"
 
+find-cache-dir@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.1.0.tgz#9935894999debef4cf9f677fdf646d002c4cdecb"
+  integrity sha512-zw+EFiNBNPgI2NTrKkDd1xd7q0cs6wr/iWnr/oUkI0yF9K9GqQ+riIt4aiyFaaqpaWbxPrJXHI+QvmNUQbX+0Q==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^3.0.0"
+    pkg-dir "^4.1.0"
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -6783,6 +6837,15 @@ fs-constants@^1.0.0:
   resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
   integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
 
+fs-extra@8.1.0, fs-extra@^8.0.1, fs-extra@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
 fs-extra@^7.0.1:
   version "7.0.1"
   resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
@@ -6792,15 +6855,6 @@ fs-extra@^7.0.1:
     jsonfile "^4.0.0"
     universalify "^0.1.0"
 
-fs-extra@^8.0.1, fs-extra@^8.1.0:
-  version "8.1.0"
-  resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
-  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
-  dependencies:
-    graceful-fs "^4.2.0"
-    jsonfile "^4.0.0"
-    universalify "^0.1.0"
-
 fs-minipass@^1.2.5:
   version "1.2.6"
   resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
@@ -8335,6 +8389,11 @@ is-lower-case@^1.1.0:
   dependencies:
     lower-case "^1.1.0"
 
+is-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
+  integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
+
 is-negated-glob@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
@@ -9960,6 +10019,13 @@ make-dir@^2.0.0, make-dir@^2.1.0:
     pify "^4.0.1"
     semver "^5.6.0"
 
+make-dir@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801"
+  integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==
+  dependencies:
+    semver "^6.0.0"
+
 make-error@1.x, make-error@^1.1.1:
   version "1.3.5"
   resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
@@ -12116,7 +12182,7 @@ pkg-dir@^3.0.0:
   dependencies:
     find-up "^3.0.0"
 
-pkg-dir@^4.2.0:
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
   version "4.2.0"
   resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
   integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@@ -13057,13 +13123,20 @@ resolve@1.1.7:
   resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
   integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
 
-resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0:
+resolve@1.12.0, resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0:
   version "1.12.0"
   resolved "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
   integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
   dependencies:
     path-parse "^1.0.6"
 
+resolve@^1.11.1:
+  version "1.13.1"
+  resolved "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16"
+  integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==
+  dependencies:
+    path-parse "^1.0.6"
+
 restore-cursor@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -13127,6 +13200,51 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+rollup-plugin-terser@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.1.2.tgz#3e41256205cb75f196fc70d4634227d1002c255c"
+  integrity sha512-sWKBCOS+vUkRtHtEiJPAf+WnBqk/C402fBD9AVHxSIXMqjsY7MnYWKYEUqGixtr0c8+1DjzUEPlNgOYQPVrS1g==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    jest-worker "^24.6.0"
+    rollup-pluginutils "^2.8.1"
+    serialize-javascript "^1.7.0"
+    terser "^4.1.0"
+
+rollup-plugin-typescript2@^0.25.3:
+  version "0.25.3"
+  resolved "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.25.3.tgz#a5fb2f0f85488789334ce540abe6c7011cbdf40f"
+  integrity sha512-ADkSaidKBovJmf5VBnZBZe+WzaZwofuvYdzGAKTN/J4hN7QJCFYAq7IrH9caxlru6T5qhX41PNFS1S4HqhsGQg==
+  dependencies:
+    find-cache-dir "^3.0.0"
+    fs-extra "8.1.0"
+    resolve "1.12.0"
+    rollup-pluginutils "2.8.1"
+    tslib "1.10.0"
+
+rollup-pluginutils@2.8.1:
+  version "2.8.1"
+  resolved "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97"
+  integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==
+  dependencies:
+    estree-walker "^0.6.1"
+
+rollup-pluginutils@^2.8.1:
+  version "2.8.2"
+  resolved "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
+  integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
+  dependencies:
+    estree-walker "^0.6.1"
+
+rollup@^1.27.9:
+  version "1.27.9"
+  resolved "https://registry.npmjs.org/rollup/-/rollup-1.27.9.tgz#742f1234c1fa935f35149a433807da675b10f9a6"
+  integrity sha512-8AfW4cJTPZfG6EXWwT/ujL4owUsDI1Xl8J1t+hvK4wDX81F5I4IbwP9gvGbHzxnV19fnU4rRABZQwZSX9J402Q==
+  dependencies:
+    "@types/estree" "*"
+    "@types/node" "*"
+    acorn "^7.1.0"
+
 rsvp@^4.8.4:
   version "4.8.5"
   resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
@@ -13163,7 +13281,7 @@ rxjs@6.4.0:
   dependencies:
     tslib "^1.9.0"
 
-rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2:
+rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2, rxjs@^6.5.3:
   version "6.5.3"
   resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
   integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
@@ -14378,6 +14496,15 @@ terser@^4.0.0, terser@^4.1.2:
     source-map "~0.6.1"
     source-map-support "~0.5.12"
 
+terser@^4.1.0:
+  version "4.4.2"
+  resolved "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz#448fffad0245f4c8a277ce89788b458bfd7706e8"
+  integrity sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
 test-exclude@^5.2.3:
   version "5.2.3"
   resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0"