From 63c4db6d71b98523753c51747e308682d9a2e8a0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 17:36:46 +0100 Subject: [PATCH] Move to angular cli --- client/.angular-cli.json | 66 ++ client/.bootstraprc | 125 --- client/package.json | 12 +- .../following-add.component.scss | 4 +- .../+admin/follows/shared/follow.service.ts | 13 +- client/src/app/+admin/index.ts | 1 - client/src/app/+admin/jobs/job.routes.ts | 5 +- .../src/app/+admin/jobs/shared/job.service.ts | 4 +- .../app/+admin/users/shared/user.service.ts | 3 +- .../users/user-edit/user-edit.component.scss | 3 + .../users/user-list/user-list.component.scss | 19 +- .../account-change-password.component.scss | 3 + .../account-details.component.scss | 3 + .../account-settings.component.scss | 3 + .../account-videos.component.scss | 3 + client/src/app/account/account.module.ts | 1 - client/src/app/app-routing.module.ts | 2 +- client/src/app/app.component.scss | 3 + client/src/app/app.module.ts | 84 +- client/src/app/app.resolver.ts | 20 - client/src/app/app.service.ts | 48 - client/src/app/core/auth/auth.service.ts | 10 +- client/src/app/core/core.module.ts | 1 - client/src/app/core/server/server.service.ts | 6 +- client/src/app/environment.ts | 77 -- client/src/app/header/header.component.scss | 3 + client/src/app/login/login.component.scss | 3 + client/src/app/menu/menu.component.scss | 3 + .../src/app/shared/account/account.model.ts | 3 +- .../{host.validator.ts => host.ts} | 0 .../app/shared/forms/form-validators/index.ts | 2 +- .../form-validators/validator-message.ts | 5 + .../app/shared/forms/form-validators/video.ts | 6 - .../src/app/shared/misc/button.component.scss | 3 + client/src/app/shared/users/user.service.ts | 8 +- .../shared/video-abuse/video-abuse.service.ts | 12 +- .../video-blacklist.service.ts | 13 +- .../video/video-miniature.component.scss | 3 + .../video/video-thumbnail.component.scss | 3 + client/src/app/shared/video/video.model.ts | 5 +- client/src/app/shared/video/video.service.ts | 6 +- client/src/app/signup/signup.component.scss | 3 + client/src/app/videos/+video-edit/index.ts | 2 - .../shared/video-description.component.scss | 3 + .../shared/video-edit.component.scss | 3 + .../shared/video-edit.component.ts | 9 +- .../+video-edit/video-add.component.scss | 3 + .../videos/+video-edit/video-add.component.ts | 5 +- .../+video-edit/video-update.component.ts | 6 +- client/src/app/videos/+video-watch/index.ts | 1 - .../video-download.component.scss | 3 + .../+video-watch/video-watch.component.scss | 3 + .../+video-watch/video-watch.component.ts | 4 +- .../src/app/videos/videos-routing.module.ts | 6 +- .../assets/player/peertube-videojs-plugin.ts | 4 +- client/src/custom-typings.d.ts | 134 --- client/src/environments/environment.prod.ts | 4 + client/src/environments/environment.ts | 9 + client/src/index.html | 6 +- client/src/main.browser.aot.ts | 35 - client/src/main.browser.ts | 45 - client/src/main.ts | 12 + client/src/polyfills.browser.ts | 38 - client/src/polyfills.ts | 66 ++ client/src/sass/application.scss | 11 +- client/src/sass/{ => include}/_mixins.scss | 2 + client/src/sass/{ => include}/_variables.scss | 0 client/src/sass/pre-customizations.scss | 877 ------------------ client/src/standalone/videos/embed.ts | 2 +- client/src/tsconfig.app.json | 13 + client/src/typings.d.ts | 5 + client/tsconfig.json | 33 +- client/tsconfig.webpack.json | 37 - client/yarn.lock | 759 +++++++++++++-- 74 files changed, 1028 insertions(+), 1712 deletions(-) create mode 100644 client/.angular-cli.json delete mode 100644 client/.bootstraprc delete mode 100644 client/src/app/+admin/index.ts delete mode 100644 client/src/app/app.resolver.ts delete mode 100644 client/src/app/app.service.ts delete mode 100644 client/src/app/environment.ts rename client/src/app/shared/forms/form-validators/{host.validator.ts => host.ts} (100%) create mode 100644 client/src/app/shared/forms/form-validators/validator-message.ts delete mode 100644 client/src/app/videos/+video-edit/index.ts delete mode 100644 client/src/app/videos/+video-watch/index.ts delete mode 100644 client/src/custom-typings.d.ts create mode 100644 client/src/environments/environment.prod.ts create mode 100644 client/src/environments/environment.ts delete mode 100644 client/src/main.browser.aot.ts delete mode 100644 client/src/main.browser.ts create mode 100644 client/src/main.ts delete mode 100644 client/src/polyfills.browser.ts create mode 100644 client/src/polyfills.ts rename client/src/sass/{ => include}/_mixins.scss (98%) rename client/src/sass/{ => include}/_variables.scss (100%) delete mode 100644 client/src/sass/pre-customizations.scss create mode 100644 client/src/tsconfig.app.json create mode 100644 client/src/typings.d.ts delete mode 100644 client/tsconfig.webpack.json diff --git a/client/.angular-cli.json b/client/.angular-cli.json new file mode 100644 index 000000000..3f94909e1 --- /dev/null +++ b/client/.angular-cli.json @@ -0,0 +1,66 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "project": { + "name": "PeerTube" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "deployUrl": "client/", + "assets": [ + "assets" + ], + "index": "index.html", + "main": "main.ts", + "polyfills": "polyfills.ts", + "test": "test.ts", + "tsconfig": "tsconfig.app.json", + "testTsconfig": "tsconfig.spec.json", + "prefix": "app", + "styles": [ + "sass/application.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "sass/include" + ] + }, + "scripts": [], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "e2e": { + "protractor": { + "config": "./protractor.conf.js" + } + }, + "lint": [ + { + "project": "src/tsconfig.app.json", + "exclude": "**/node_modules/**" + }, + { + "project": "src/tsconfig.spec.json", + "exclude": "**/node_modules/**" + }, + { + "project": "e2e/tsconfig.e2e.json", + "exclude": "**/node_modules/**" + } + ], + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "scss", + "component": { + } + } +} diff --git a/client/.bootstraprc b/client/.bootstraprc deleted file mode 100644 index cc6768d43..000000000 --- a/client/.bootstraprc +++ /dev/null @@ -1,125 +0,0 @@ ---- -# Output debugging info -# loglevel: debug - -# Major version of Bootstrap: 3 or 4 -bootstrapVersion: 3 - -# If Bootstrap version 3 is used - turn on/off custom icon font path -useCustomIconFontPath: true - -# Webpack loaders, order matters -styleLoaders: - - style-loader - - css-loader - - sass-loader - -# Extract styles to stand-alone css file -# Different settings for different environments can be used, -# It depends on value of NODE_ENV environment variable -# This param can also be set in webpack config: -# entry: 'bootstrap-loader/extractStyles' -# extractStyles: false -env: - development: - extractStyles: false - production: - extractStyles: true - -# Customize Bootstrap variables that get imported before the original Bootstrap variables. -# Thus original Bootstrap variables can depend on values from here. All the bootstrap -# variables are configured with !default, and thus, if you define the variable here, then -# that value is used, rather than the default. However, many bootstrap variables are derived -# from other bootstrap variables, and thus, you want to set this up before we load the -# official bootstrap versions. -# For example, _variables.scss contains: -# $input-color: $gray !default; -# This means you can define $input-color before we load _variables.scss -preBootstrapCustomizations: ./src/sass/pre-customizations.scss - -# This gets loaded after bootstrap/variables is loaded and before bootstrap is loaded. -# A good example of this is when you want to override a bootstrap variable to be based -# on the default value of bootstrap. This is pretty specialized case. Thus, you normally -# just override bootrap variables in preBootstrapCustomizations so that derived -# variables will use your definition. -# -# For example, in _variables.scss: -# $input-height: (($font-size-base * $line-height) + ($input-padding-y * 2) + ($border-width * 2)) !default; -# This means that you could define this yourself in preBootstrapCustomizations. Or you can do -# this in bootstrapCustomizations to make the input height 10% bigger than the default calculation. -# Thus you can leverage the default calculations. -# $input-height: $input-height * 1.10; -# bootstrapCustomizations: ./app/styles/bootstrap/customizations.scss - -# Import your custom styles here. You have access to all the bootstrap variables. If you require -# your sass files separately, you will not have access to the bootstrap variables, mixins, clases, etc. -# Usually this endpoint-file contains list of @imports of your application styles. -appStyles: ./src/sass/application.scss - -### Bootstrap styles -styles: - - # Mixins - mixins: true - - # Reset and dependencies - normalize: true - print: true - glyphicons: true - - # Core CSS - scaffolding: true - type: true - code: false - grid: true - tables: true - forms: true - buttons: true - - # Components - component-animations: false - dropdowns: true - button-groups: true - input-groups: true - navs: true - navbar: false - breadcrumbs: false - pagination: false - pager: false - labels: false - badges: false - jumbotron: false - thumbnails: false - alerts: true - progress-bars: false - media: true - list-group: false - panels: true - wells: false - responsive-embed: false - close: true - - # Components w/ JavaScript - modals: true - tooltip: false - popovers: false - carousel: false - - # Utility classes - utilities: true - responsive-utilities: true - -### Bootstrap scripts -scripts: - transition: false - alert: false - button: false - carousel: false - collapse: false - dropdown: false - modal: false - tooltip: false - popover: false - scrollspy: false - tab: false - affix: false diff --git a/client/package.json b/client/package.json index 7441d15ec..aa754a593 100644 --- a/client/package.json +++ b/client/package.json @@ -5,7 +5,7 @@ "licence": "GPLv3", "author": { "name": "Florian Bigard", - "email": "florian.bigard@gmail.com", + "email": "me@florianbigard.com", "url": "http://github.com/Chocobozzz" }, "repository": { @@ -15,19 +15,21 @@ "scripts": { "lint": "standard && tslint --type-check --project ./tsconfig.json -c ./tslint.json 'src/app/**/*.ts'", "webpack": "webpack", - "webpack-dev-server": "webpack-dev-server", + "webpack-dev-server<": "webpack-dev-server", "postinstall": "npm rebuild node-sass" }, "license": "GPLv3", "dependencies": {}, "devDependencies": { "@angular/animations": "~4.4.0", + "@angular/cli": "^1.6.0", "@angular/common": "~4.4.0", "@angular/compiler": "~4.4.0", "@angular/compiler-cli": "~4.4.0", "@angular/core": "~4.4.0", "@angular/forms": "~4.4.0", "@angular/http": "~4.4.0", + "@angular/language-service": "^5.1.0", "@angular/platform-browser": "~4.4.0", "@angular/platform-browser-dynamic": "~4.4.0", "@angular/router": "~4.4.0", @@ -50,9 +52,7 @@ "babel-core": "^6.25.0", "babel-loader": "^7.1.0", "babel-preset-env": "^1.5.2", - "bootstrap": "^3.3.6", - "bootstrap-loader": "2.2.0", - "bootstrap-sass": "^3.3.6", + "bootstrap-sass": "^3.3.7", "codelyzer": "^3.0.0-beta.4", "copy-webpack-plugin": "^4.0.0", "core-js": "^2.4.1", @@ -76,6 +76,7 @@ "ngx-pipes": "^2.0.5", "node-sass": "^4.1.1", "normalize.css": "^7.0.0", + "npm-font-source-sans-pro": "^1.0.2", "optimize-js-plugin": "0.0.4", "primeng": "^4.2.0", "purify-css": "^1.2.5", @@ -88,7 +89,6 @@ "sass-resources-loader": "^1.2.1", "script-ext-html-webpack-plugin": "^1.3.2", "source-map-loader": "^0.2.1", - "source-sans-pro": "^2.0.10", "standard": "^10.0.0", "string-replace-loader": "^1.0.3", "style-loader": "^0.19.0", diff --git a/client/src/app/+admin/follows/following-add/following-add.component.scss b/client/src/app/+admin/follows/following-add/following-add.component.scss index 2cb3efe28..7594b502c 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.scss +++ b/client/src/app/+admin/follows/following-add/following-add.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + textarea { height: 250px; } @@ -6,4 +9,3 @@ input[type=submit] { @include peertube-button; @include orange-button; } - diff --git a/client/src/app/+admin/follows/shared/follow.service.ts b/client/src/app/+admin/follows/shared/follow.service.ts index 0bfbe8eb6..3a430ff64 100644 --- a/client/src/app/+admin/follows/shared/follow.service.ts +++ b/client/src/app/+admin/follows/shared/follow.service.ts @@ -1,17 +1,16 @@ -import { Injectable } from '@angular/core' import { HttpClient, HttpParams } from '@angular/common/http' -import { Observable } from 'rxjs/Observable' +import { Injectable } from '@angular/core' +import { SortMeta } from 'primeng/primeng' import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' - -import { SortMeta } from 'primeng/primeng' - -import { RestExtractor, RestPagination, RestService } from '../../../shared' +import { Observable } from 'rxjs/Observable' import { AccountFollow, ResultList } from '../../../../../../shared' +import { environment } from '../../../../environments/environment' +import { RestExtractor, RestPagination, RestService } from '../../../shared' @Injectable() export class FollowService { - private static BASE_APPLICATION_URL = API_URL + '/api/v1/server' + private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/server' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/+admin/index.ts b/client/src/app/+admin/index.ts deleted file mode 100644 index b628a3662..000000000 --- a/client/src/app/+admin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './admin.module' diff --git a/client/src/app/+admin/jobs/job.routes.ts b/client/src/app/+admin/jobs/job.routes.ts index a7bf2b221..331dc2af2 100644 --- a/client/src/app/+admin/jobs/job.routes.ts +++ b/client/src/app/+admin/jobs/job.routes.ts @@ -1,9 +1,6 @@ import { Routes } from '@angular/router' - -import { UserRightGuard } from '../../core' -import { FollowingAddComponent } from './following-add' import { UserRight } from '../../../../../shared' -import { FollowingListComponent } from './following-list/following-list.component' +import { UserRightGuard } from '../../core' import { JobsComponent } from './job.component' import { JobsListComponent } from './jobs-list/jobs-list.component' diff --git a/client/src/app/+admin/jobs/shared/job.service.ts b/client/src/app/+admin/jobs/shared/job.service.ts index 0cfbdbbea..61ee16077 100644 --- a/client/src/app/+admin/jobs/shared/job.service.ts +++ b/client/src/app/+admin/jobs/shared/job.service.ts @@ -6,12 +6,12 @@ import 'rxjs/add/operator/map' import { Observable } from 'rxjs/Observable' import { ResultList } from '../../../../../../shared' import { Job } from '../../../../../../shared/models/job.model' - +import { environment } from '../../../../environments/environment' import { RestExtractor, RestPagination, RestService } from '../../../shared' @Injectable() export class JobService { - private static BASE_JOB_URL = API_URL + '/api/v1/jobs' + private static BASE_JOB_URL = environment.apiUrl + '/api/v1/jobs' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts index dc77cc1d8..6536546fb 100644 --- a/client/src/app/+admin/users/shared/user.service.ts +++ b/client/src/app/+admin/users/shared/user.service.ts @@ -6,11 +6,12 @@ import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' import { Observable } from 'rxjs/Observable' import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared' +import { environment } from '../../../../environments/environment' import { RestExtractor, RestPagination, RestService, User } from '../../../shared' @Injectable() export class UserService { - private static BASE_USERS_URL = API_URL + '/api/v1/users/' + private static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/' private bytesPipe = new BytesPipe() constructor ( diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss index 68d270c19..1bb1c5f0f 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .admin-sub-title { margin-bottom: 30px; } diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss index 8b22f67ff..72d31a0cc 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/users/user-list/user-list.component.scss @@ -1,11 +1,14 @@ - .add-button { - @include peertube-button-link; - @include orange-button; +@import '_variables'; +@import '_mixins'; - .icon.icon-add { - @include icon(22px); +.add-button { + @include peertube-button-link; + @include orange-button; - margin-right: 3px; - background-image: url('../../../../assets/images/admin/add.svg'); - } + .icon.icon-add { + @include icon(22px); + + margin-right: 3px; + background-image: url('../../../../assets/images/admin/add.svg'); } +} diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss index 7a4fdb34d..f8279ffd3 100644 --- a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + input[type=password] { @include peertube-input-text(340px); display: block; diff --git a/client/src/app/account/account-settings/account-details/account-details.component.scss b/client/src/app/account/account-settings/account-details/account-details.component.scss index 5c369f968..1bdb19a38 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.scss +++ b/client/src/app/account/account-settings/account-details/account-details.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + label { font-size: 15px; font-weight: $font-regular; diff --git a/client/src/app/account/account-settings/account-settings.component.scss b/client/src/app/account/account-settings/account-settings.component.scss index f514809b0..7f1ade377 100644 --- a/client/src/app/account/account-settings/account-settings.component.scss +++ b/client/src/app/account/account-settings/account-settings.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .user { display: flex; diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index 5459014a6..28b298c3d 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .action-selection-mode { width: 174px; display: flex; diff --git a/client/src/app/account/account.module.ts b/client/src/app/account/account.module.ts index 020199e23..2299c1919 100644 --- a/client/src/app/account/account.module.ts +++ b/client/src/app/account/account.module.ts @@ -5,7 +5,6 @@ import { AccountChangePasswordComponent } from './account-settings/account-chang import { AccountDetailsComponent } from './account-settings/account-details/account-details.component' import { AccountSettingsComponent } from './account-settings/account-settings.component' import { AccountComponent } from './account.component' -import { AccountService } from './account.service' import { AccountVideosComponent } from './account-videos/account-videos.component' @NgModule({ diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index fe72c9181..f31b51e23 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -11,7 +11,7 @@ const routes: Routes = [ }, { path: 'admin', - loadChildren: './+admin#AdminModule' + loadChildren: './+admin/admin.module#AdminModule' } ] diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index c0ec2025d..2aadb1c97 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .main-row { min-height: calc(100vh - #{$header-height} - #{$footer-height} - #{$footer-margin}); } diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 1326e3411..e69edbc4b 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -1,18 +1,10 @@ -import { ApplicationRef, NgModule } from '@angular/core' +import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' -import { - removeNgStyles, - createNewHosts, - createInputTransfer -} from '@angularclass/hmr' import { MetaModule, MetaLoader, MetaStaticLoader, PageTitlePositioning } from '@ngx-meta/core' -import 'bootstrap-loader' -import { ENV_PROVIDERS } from './environment' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' -import { AppState, InternalStateType } from './app.service' import { AccountModule } from './account' import { CoreModule } from './core' @@ -35,17 +27,6 @@ export function metaFactory (): MetaLoader { }) } -type StoreType = { - state: InternalStateType, - restoreInputValues: () => void, - disposeOldHosts: () => void -} - -// Application wide providers -const APP_PROVIDERS = [ - AppState -] - @NgModule({ bootstrap: [ AppComponent ], declarations: [ @@ -74,65 +55,6 @@ const APP_PROVIDERS = [ useFactory: (metaFactory) }) ], - providers: [ // expose our Services and Providers into Angular's dependency injection - ENV_PROVIDERS, - APP_PROVIDERS - ] + providers: [ ] }) -export class AppModule { - constructor ( - public appRef: ApplicationRef, - public appState: AppState - ) {} - - public hmrOnInit (store: StoreType) { - if (!store || !store.state) { - return - } - console.log('HMR store', JSON.stringify(store, null, 2)) - /** - * Set state - */ - this.appState._state = store.state - /** - * Set input values - */ - if ('restoreInputValues' in store) { - let restoreInputValues = store.restoreInputValues - setTimeout(restoreInputValues) - } - - this.appRef.tick() - delete store.state - delete store.restoreInputValues - } - - public hmrOnDestroy (store: StoreType) { - const cmpLocation = this.appRef.components.map((cmp) => cmp.location.nativeElement) - /** - * Save state - */ - const state = this.appState._state - store.state = state - /** - * Recreate root elements - */ - store.disposeOldHosts = createNewHosts(cmpLocation) - /** - * Save input values - */ - store.restoreInputValues = createInputTransfer() - /** - * Remove styles - */ - removeNgStyles() - } - - public hmrAfterDestroy (store: StoreType) { - /** - * Display new elements - */ - store.disposeOldHosts() - delete store.disposeOldHosts - } -} +export class AppModule {} diff --git a/client/src/app/app.resolver.ts b/client/src/app/app.resolver.ts deleted file mode 100644 index bc054e90b..000000000 --- a/client/src/app/app.resolver.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* tslint:disable */ - -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router' -import { Injectable } from '@angular/core' -import { Observable } from 'rxjs/Observable' -import 'rxjs/add/observable/of' - -@Injectable() -export class DataResolver implements Resolve { - public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - return Observable.of({ res: 'I am data'}) - } -} - -/** - * An array of services to resolve routes with data. - */ -export const APP_RESOLVER_PROVIDERS = [ - DataResolver -] diff --git a/client/src/app/app.service.ts b/client/src/app/app.service.ts deleted file mode 100644 index abffc87f1..000000000 --- a/client/src/app/app.service.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* tslint:disable */ - -import { Injectable } from '@angular/core' - -export type InternalStateType = { - [key: string]: any -} - -@Injectable() -export class AppState { - - public _state: InternalStateType = { } - - /** - * Already return a clone of the current state. - */ - public get state() { - return this._state = this._clone(this._state) - } - /** - * Never allow mutation - */ - public set state(value) { - throw new Error('do not mutate the `.state` directly') - } - - public get(prop?: any) { - /** - * Use our state getter for the clone. - */ - const state = this.state - return state.hasOwnProperty(prop) ? state[prop] : state - } - - public set(prop: string, value: any) { - /** - * Internally mutate our state. - */ - return this._state[prop] = value - } - - private _clone(object: InternalStateType) { - /** - * Simple object clone. - */ - return JSON.parse(JSON.stringify( object )) - } -} diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index e887dde1f..e2b8b6ba5 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -1,7 +1,6 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { Router } from '@angular/router' - import { NotificationsService } from 'angular2-notifications' import 'rxjs/add/observable/throw' import 'rxjs/add/operator/do' @@ -13,10 +12,9 @@ import { Subject } from 'rxjs/Subject' import { OAuthClientLocal, User as UserServerModel, UserRefreshToken, UserRole, VideoChannel } from '../../../../../shared' import { Account } from '../../../../../shared/models/accounts' import { UserLogin } from '../../../../../shared/models/users/user-login.model' -// Do not use the barrel (dependency loop) +import { environment } from '../../../environments/environment' import { RestExtractor } from '../../shared/rest' import { UserConstructorHash } from '../../shared/users/user.model' - import { AuthStatus } from './auth-status.model' import { AuthUser } from './auth-user.model' @@ -43,9 +41,9 @@ interface UserLoginWithUserInformation extends UserLogin { @Injectable() export class AuthService { - private static BASE_CLIENT_URL = API_URL + '/api/v1/oauth-clients/local' - private static BASE_TOKEN_URL = API_URL + '/api/v1/users/token' - private static BASE_USER_INFORMATION_URL = API_URL + '/api/v1/users/me' + private static BASE_CLIENT_URL = environment.apiUrl + '/api/v1/oauth-clients/local' + private static BASE_TOKEN_URL = environment.apiUrl + '/api/v1/users/token' + private static BASE_USER_INFORMATION_URL = environment.apiUrl + '/api/v1/users/me' loginChangedSource: Observable userInformationLoaded = new ReplaySubject(1) diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 75262e6cf..a58fe6ebe 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -11,7 +11,6 @@ import { AuthService } from './auth' import { LoginGuard, UserRightGuard } from './routing' import { ServerService } from './server' import { ConfirmComponent, ConfirmService } from './confirm' -import { MenuComponent, MenuAdminComponent } from './menu' import { throwIfAlreadyLoaded } from './module-import-guard' @NgModule({ diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 16e0595b6..a5be9e199 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -2,13 +2,13 @@ import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' import 'rxjs/add/operator/do' import { ReplaySubject } from 'rxjs/ReplaySubject' - import { ServerConfig } from '../../../../../shared' +import { environment } from '../../../environments/environment' @Injectable() export class ServerService { - private static BASE_CONFIG_URL = API_URL + '/api/v1/config/' - private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' + private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' + private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' videoPrivaciesLoaded = new ReplaySubject(1) videoCategoriesLoaded = new ReplaySubject(1) diff --git a/client/src/app/environment.ts b/client/src/app/environment.ts deleted file mode 100644 index dd327a62e..000000000 --- a/client/src/app/environment.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* tslint:disable */ - -/** - * Angular 2 - */ -import { - enableDebugTools, - disableDebugTools -} from '@angular/platform-browser'; -import { - ApplicationRef, - enableProdMode -} from '@angular/core'; -/** - * Environment Providers - */ -let PROVIDERS: any[] = [ - /** - * Common env directives - */ -]; - -/** - * Angular debug tools in the dev console - * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md - */ -let _decorateModuleRef = (value: T): T => { return value; }; - -if ('production' === ENV) { - enableProdMode(); - - /** - * Production - */ - _decorateModuleRef = (modRef: any) => { - disableDebugTools(); - - return modRef; - }; - - PROVIDERS = [ - ...PROVIDERS, - /** - * Custom providers in production. - */ - ]; - -} else { - - _decorateModuleRef = (modRef: any) => { - const appRef = modRef.injector.get(ApplicationRef); - const cmpRef = appRef.components[0]; - - let _ng = ( window).ng; - enableDebugTools(cmpRef); - ( window).ng.probe = _ng.probe; - ( window).ng.coreTokens = _ng.coreTokens; - return modRef; - }; - - /** - * Development - */ - PROVIDERS = [ - ...PROVIDERS, - /** - * Custom providers in development. - */ - ]; - -} - -export const decorateModuleRef = _decorateModuleRef; - -export const ENV_PROVIDERS = [ - ...PROVIDERS -]; diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index fba70dd2f..1b67042fc 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + #search-video { @include peertube-input-text($search-input-width); margin-right: 15px; diff --git a/client/src/app/login/login.component.scss b/client/src/app/login/login.component.scss index 3b4326de4..efec6b706 100644 --- a/client/src/app/login/login.component.scss +++ b/client/src/app/login/login.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + input:not([type=submit]) { @include peertube-input-text(340px); display: block; diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 97ceadde3..8cb94b3dd 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + menu { background-color: $black-background; margin: 0; diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts index 0b008188a..9d1fd3e1d 100644 --- a/client/src/app/shared/account/account.model.ts +++ b/client/src/app/shared/account/account.model.ts @@ -1,5 +1,6 @@ import { Account as ServerAccount } from '../../../../../shared/models/accounts/account.model' import { Avatar } from '../../../../../shared/models/avatars/avatar.model' +import { environment } from '../../../environments/environment' export class Account implements ServerAccount { id: number @@ -15,6 +16,6 @@ export class Account implements ServerAccount { static GET_ACCOUNT_AVATAR_PATH (account: Account) { if (account && account.avatar) return account.avatar.path - return API_URL + '/client/assets/images/default-avatar.png' + return environment.apiUrl + '/client/assets/images/default-avatar.png' } } diff --git a/client/src/app/shared/forms/form-validators/host.validator.ts b/client/src/app/shared/forms/form-validators/host.ts similarity index 100% rename from client/src/app/shared/forms/form-validators/host.validator.ts rename to client/src/app/shared/forms/form-validators/host.ts diff --git a/client/src/app/shared/forms/form-validators/index.ts b/client/src/app/shared/forms/form-validators/index.ts index efe77d4ae..09ae86f8a 100644 --- a/client/src/app/shared/forms/form-validators/index.ts +++ b/client/src/app/shared/forms/form-validators/index.ts @@ -1,4 +1,4 @@ -export * from './host.validator' +export * from './host' export * from './user' export * from './video-abuse' export * from './video' diff --git a/client/src/app/shared/forms/form-validators/validator-message.ts b/client/src/app/shared/forms/form-validators/validator-message.ts new file mode 100644 index 000000000..5ce45833b --- /dev/null +++ b/client/src/app/shared/forms/form-validators/validator-message.ts @@ -0,0 +1,5 @@ +export type ValidatorMessage = { + [ id: string ]: { + [ error: string ]: string + } +} diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts index 45da7df4a..500b5bc5f 100644 --- a/client/src/app/shared/forms/form-validators/video.ts +++ b/client/src/app/shared/forms/form-validators/video.ts @@ -1,11 +1,5 @@ import { Validators } from '@angular/forms' -export type ValidatorMessage = { - [ id: string ]: { - [ error: string ]: string - } -} - export const VIDEO_NAME = { VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ], MESSAGES: { diff --git a/client/src/app/shared/misc/button.component.scss b/client/src/app/shared/misc/button.component.scss index 5fcae4f10..c380c7ae1 100644 --- a/client/src/app/shared/misc/button.component.scss +++ b/client/src/app/shared/misc/button.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .action-button { @include peertube-button-link; diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts index 6d1017fc9..d97edbcbe 100644 --- a/client/src/app/shared/users/user.service.ts +++ b/client/src/app/shared/users/user.service.ts @@ -1,14 +1,14 @@ -import { Injectable } from '@angular/core' import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' - -import { RestExtractor } from '../rest' import { UserCreate, UserUpdateMe } from '../../../../../shared' +import { environment } from '../../../environments/environment' +import { RestExtractor } from '../rest' @Injectable() export class UserService { - static BASE_USERS_URL = API_URL + '/api/v1/users/' + static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/shared/video-abuse/video-abuse.service.ts b/client/src/app/shared/video-abuse/video-abuse.service.ts index 8d979de31..96a1f1fd2 100644 --- a/client/src/app/shared/video-abuse/video-abuse.service.ts +++ b/client/src/app/shared/video-abuse/video-abuse.service.ts @@ -1,19 +1,17 @@ -import { Injectable } from '@angular/core' import { HttpClient, HttpParams } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { SortMeta } from 'primeng/components/common/sortmeta' import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' import { Observable } from 'rxjs/Observable' - -import { SortMeta } from 'primeng/components/common/sortmeta' - -import { AuthService } from '../core' +import { ResultList, VideoAbuse } from '../../../../../shared' import { RestExtractor, RestPagination, RestService } from '../rest' import { Utils } from '../utils' -import { ResultList, VideoAbuse } from '../../../../../shared' +import { environment } from '../../../environments/environment' @Injectable() export class VideoAbuseService { - private static BASE_VIDEO_ABUSE_URL = API_URL + '/api/v1/videos/' + private static BASE_VIDEO_ABUSE_URL = environment.apiUrl + '/api/v1/videos/' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts index 17373d52e..1231690aa 100644 --- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts +++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts @@ -1,18 +1,17 @@ -import { Injectable } from '@angular/core' import { HttpClient, HttpParams } from '@angular/common/http' -import { Observable } from 'rxjs/Observable' +import { Injectable } from '@angular/core' +import { SortMeta } from 'primeng/components/common/sortmeta' import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' - -import { SortMeta } from 'primeng/components/common/sortmeta' - +import { Observable } from 'rxjs/Observable' +import { BlacklistedVideo, ResultList } from '../../../../../shared' +import { environment } from '../../../environments/environment' import { RestExtractor, RestPagination, RestService } from '../rest' import { Utils } from '../utils' -import { BlacklistedVideo, ResultList } from '../../../../../shared' @Injectable() export class VideoBlacklistService { - private static BASE_VIDEOS_URL = API_URL + '/api/v1/videos/' + private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/shared/video/video-miniature.component.scss b/client/src/app/shared/video/video-miniature.component.scss index 37e84897b..49ba1e51c 100644 --- a/client/src/app/shared/video/video-miniature.component.scss +++ b/client/src/app/shared/video/video-miniature.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .video-miniature { display: inline-block; padding-right: 15px; diff --git a/client/src/app/shared/video/video-thumbnail.component.scss b/client/src/app/shared/video/video-thumbnail.component.scss index ab4f9bcb1..0fc2df220 100644 --- a/client/src/app/shared/video/video-thumbnail.component.scss +++ b/client/src/app/shared/video/video-thumbnail.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .video-thumbnail { display: inline-block; position: relative; diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index d86ef8f92..32c33829d 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts @@ -1,6 +1,7 @@ -import { Video as VideoServerModel } from '../../../../../shared' import { User } from '../' +import { Video as VideoServerModel } from '../../../../../shared' import { Account } from '../../../../../shared/models/accounts' +import { environment } from '../../../environments/environment' export class Video implements VideoServerModel { accountName: string @@ -48,7 +49,7 @@ export class Video implements VideoServerModel { } constructor (hash: VideoServerModel) { - let absoluteAPIUrl = API_URL + let absoluteAPIUrl = environment.apiUrl if (!absoluteAPIUrl) { // The API is on the same domain absoluteAPIUrl = window.location.origin diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 1a0644c3d..91dd3977a 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -9,9 +9,9 @@ import { UserVideoRateUpdate } from '../../../../../shared/models/videos/user-vi import { UserVideoRate } from '../../../../../shared/models/videos/user-video-rate.model' import { VideoRateType } from '../../../../../shared/models/videos/video-rate.type' import { VideoUpdate } from '../../../../../shared/models/videos/video-update.model' +import { environment } from '../../../environments/environment' import { RestExtractor } from '../rest/rest-extractor.service' import { RestService } from '../rest/rest.service' -import { Search } from '../header/search.model' import { UserService } from '../users/user.service' import { SortField } from './sort-field.type' import { VideoDetails } from './video-details.model' @@ -21,7 +21,7 @@ import { Video } from './video.model' @Injectable() export class VideoService { - private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' + private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' constructor ( private authHttp: HttpClient, @@ -118,7 +118,7 @@ export class VideoService { loadCompleteDescription (descriptionPath: string) { return this.authHttp - .get(API_URL + descriptionPath) + .get(environment.apiUrl + descriptionPath) .map(res => res['description']) .catch((res) => this.restExtractor.handleError(res)) } diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/signup/signup.component.scss index 3b4326de4..efec6b706 100644 --- a/client/src/app/signup/signup.component.scss +++ b/client/src/app/signup/signup.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + input:not([type=submit]) { @include peertube-input-text(340px); display: block; diff --git a/client/src/app/videos/+video-edit/index.ts b/client/src/app/videos/+video-edit/index.ts deleted file mode 100644 index 63e0414dd..000000000 --- a/client/src/app/videos/+video-edit/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './video-add.module' -export * from './video-update.module' diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss index 2a4c8d189..d6a5190c5 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + textarea { @include peertube-input-text(100%); diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index d363499ce..f4466bdde 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .video-edit { height: 100%; diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts index 5b1cc3f9c..28c9134a7 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts @@ -2,12 +2,10 @@ import { Component, Input, OnInit } from '@angular/core' import { FormBuilder, FormControl, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { ServerService } from 'app/core' -import { VideoEdit } from 'app/shared/video/video-edit.model' import 'rxjs/add/observable/forkJoin' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' +import { ServerService } from '../../../core/server' +import { ValidatorMessage } from '../../../shared/forms/form-validators/validator-message' import { - ValidatorMessage, VIDEO_CATEGORY, VIDEO_DESCRIPTION, VIDEO_LANGUAGE, @@ -15,7 +13,8 @@ import { VIDEO_NAME, VIDEO_PRIVACY, VIDEO_TAGS -} from '../../../shared/forms/form-validators' +} from '../../../shared/forms/form-validators/video' +import { VideoEdit } from '../../../shared/video/video-edit.model' @Component({ selector: 'my-video-edit', diff --git a/client/src/app/videos/+video-edit/video-add.component.scss b/client/src/app/videos/+video-edit/video-add.component.scss index 39673b4b7..78140e0e9 100644 --- a/client/src/app/videos/+video-edit/video-add.component.scss +++ b/client/src/app/videos/+video-edit/video-add.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .upload-video-container { border-radius: 3px; background-color: #F7F7F7; diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 2bbc3de17..503f705db 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -3,13 +3,12 @@ import { Component, OnInit, ViewChild } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' import { Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from 'app/shared/video/video.service' -import { VideoCreate } from '../../../../../shared' import { VideoPrivacy } from '../../../../../shared/models/videos' import { AuthService, ServerService } from '../../core' import { FormReactive } from '../../shared' -import { ValidatorMessage } from '../../shared/forms/form-validators' +import { ValidatorMessage } from '../../shared/forms/form-validators/validator-message' import { VideoEdit } from '../../shared/video/video-edit.model' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-add', diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index d1da8b6d8..08b74f4c3 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -3,10 +3,10 @@ import { FormBuilder, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import 'rxjs/add/observable/forkJoin' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' +import { VideoPrivacy } from '../../../../../shared/models/videos' import { ServerService } from '../../core' import { FormReactive } from '../../shared' -import { ValidatorMessage } from '../../shared/forms/form-validators' +import { ValidatorMessage } from '../../shared/forms/form-validators/validator-message' import { VideoEdit } from '../../shared/video/video-edit.model' import { VideoService } from '../../shared/video/video.service' @@ -25,8 +25,6 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { validationMessages: ValidatorMessage = {} videoPrivacies = [] - fileError = '' - constructor ( private formBuilder: FormBuilder, private route: ActivatedRoute, diff --git a/client/src/app/videos/+video-watch/index.ts b/client/src/app/videos/+video-watch/index.ts deleted file mode 100644 index b19bfdb1e..000000000 --- a/client/src/app/videos/+video-watch/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './video-watch.module' diff --git a/client/src/app/videos/+video-watch/video-download.component.scss b/client/src/app/videos/+video-watch/video-download.component.scss index c9d5af9c1..7eac3d1c9 100644 --- a/client/src/app/videos/+video-watch/video-download.component.scss +++ b/client/src/app/videos/+video-watch/video-download.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + .resolution-block:not(:first-child) { margin-top: 30px; } diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 9daa757b4..52082944a 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -1,3 +1,6 @@ +@import '_variables'; +@import '_mixins'; + #video-container { background-color: #000; display: flex; diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index bec6932ae..3825e8449 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -2,10 +2,9 @@ import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/co import { ActivatedRoute, Router } from '@angular/router' import { MetaService } from '@ngx-meta/core' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from 'app/shared/video/video.service' import { Observable } from 'rxjs/Observable' import { Subscription } from 'rxjs/Subscription' -import videojs from 'video.js' +import * as videojs from 'video.js' import { UserVideoRateType, VideoRateType } from '../../../../../shared' import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' @@ -13,6 +12,7 @@ import { VideoBlacklistService } from '../../shared' import { Account } from '../../shared/account/account.model' import { VideoDetails } from '../../shared/video/video-details.model' import { Video } from '../../shared/video/video.model' +import { VideoService } from '../../shared/video/video.service' import { MarkdownService } from '../shared' import { VideoDownloadComponent } from './video-download.component' import { VideoReportComponent } from './video-report.component' diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 6910421b7..29ec5fd4f 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -46,7 +46,7 @@ const videosRoutes: Routes = [ }, { path: 'upload', - loadChildren: 'app/videos/+video-edit#VideoAddModule', + loadChildren: 'app/videos/+video-edit/video-add.module#VideoAddModule', data: { meta: { title: 'Upload a video' @@ -55,7 +55,7 @@ const videosRoutes: Routes = [ }, { path: 'edit/:uuid', - loadChildren: 'app/videos/+video-edit#VideoUpdateModule', + loadChildren: 'app/videos/+video-edit/video-update.module#VideoUpdateModule', data: { meta: { title: 'Edit a video' @@ -69,7 +69,7 @@ const videosRoutes: Routes = [ }, { path: 'watch/:uuid', - loadChildren: 'app/videos/+video-watch#VideoWatchModule', + loadChildren: 'app/videos/+video-watch/video-watch.module#VideoWatchModule', data: { preload: 3000 } diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 4898c1efe..8b50e323e 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts @@ -1,6 +1,6 @@ // Big thanks to: https://github.com/kmoskwiak/videojs-resolution-switcher -import videojs, { Player } from 'video.js' +import * as videojs from 'video.js' import * as WebTorrent from 'webtorrent' import { VideoFile } from '../../../../shared' @@ -27,7 +27,7 @@ const webtorrent = new WebTorrent({ dht: false }) const MenuItem = videojsUntyped.getComponent('MenuItem') const ResolutionMenuItem = videojsUntyped.extend(MenuItem, { - constructor: function (player: Player, options) { + constructor: function (player: videojs.Player, options) { options.selectable = true MenuItem.call(this, player, options) diff --git a/client/src/custom-typings.d.ts b/client/src/custom-typings.d.ts deleted file mode 100644 index 882d15d54..000000000 --- a/client/src/custom-typings.d.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* tslint: disable */ - -/* - * Custom Type Definitions - * When including 3rd party modules you also need to include the type definition for the module - * if they don't provide one within the module. You can try to install it with @types - -npm install @types/node -npm install @types/lodash - - * If you can't find the type definition in the registry we can make an ambient/global definition in - * this file for now. For example - -declare module 'my-module' { - export function doesSomething(value: string): string; -} - - * If you are using a CommonJS module that is using module.exports then you will have to write your - * types using export = yourObjectOrFunction with a namespace above it - * notice how we have to create a namespace that is equal to the function we're - * assigning the export to - -declare module 'jwt-decode' { - function jwtDecode(token: string): any; - namespace jwtDecode {} - export = jwtDecode; -} - - * - * If you're prototying and you will fix the types later you can also declare it as type any - * - -declare var assert: any; -declare var _: any; -declare var $: any; - - * - * If you're importing a module that uses Node.js modules which are CommonJS you need to import as - * in the files such as main.browser.ts or any file within app/ - * - -import * as _ from 'lodash' - - * You can include your type definitions in this file until you create one for the @types - * - */ - -// support NodeJS modules without type definitions -declare module '*'; - -/* -// for legacy tslint etc to understand rename 'modern-lru' with your package -// then comment out `declare module '*';`. For each new module copy/paste -// this method of creating an `any` module type definition -declare module 'modern-lru' { - let x: any; - export = x; -} -*/ - -// Extra variables that live on Global that will be replaced by webpack DefinePlugin -declare var ENV: string; -declare var API_URL: string; -declare var HMR: boolean; -declare var System: SystemJS; - -interface SystemJS { - import: (path?: string) => Promise; -} - -interface GlobalEnvironment { - ENV: string; - API_URL: string; - HMR: boolean; - SystemJS: SystemJS; - System: SystemJS; -} - -interface Es6PromiseLoader { - (id: string): (exportName?: string) => Promise; -} - -type FactoryEs6PromiseLoader = () => Es6PromiseLoader; -type FactoryPromise = () => Promise; - -type AsyncRoutes = { - [component: string]: Es6PromiseLoader | - Function | - FactoryEs6PromiseLoader | - FactoryPromise ; -}; - -type IdleCallbacks = Es6PromiseLoader | - Function | - FactoryEs6PromiseLoader | - FactoryPromise ; - -interface WebpackModule { - hot: { - data?: any, - idle: any, - accept(dependencies?: string | string[], callback?: (updatedDependencies?: any) => void): void; - decline(deps?: any | string | string[]): void; - dispose(callback?: (data?: any) => void): void; - addDisposeHandler(callback?: (data?: any) => void): void; - removeDisposeHandler(callback?: (data?: any) => void): void; - check(autoApply?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; - apply(options?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; - status(callback?: (status?: string) => void): void | string; - removeStatusHandler(callback?: (status?: string) => void): void; - }; -} - -interface WebpackRequire { - (id: string): any; - (paths: string[], callback: (...modules: any[]) => void): void; - ensure(ids: string[], callback: (req: WebpackRequire) => void, chunkName?: string): void; - context(directory: string, useSubDirectories?: boolean, regExp?: RegExp): WebpackContext; -} - -interface WebpackContext extends WebpackRequire { - keys(): string[]; -} - -interface ErrorStackTraceLimit { - stackTraceLimit: number; -} - -// Extend typings -interface NodeRequire extends WebpackRequire {} -interface ErrorConstructor extends ErrorStackTraceLimit {} -interface NodeRequireFunction extends Es6PromiseLoader {} -interface NodeModule extends WebpackModule {} -interface Global extends GlobalEnvironment {} diff --git a/client/src/environments/environment.prod.ts b/client/src/environments/environment.prod.ts new file mode 100644 index 000000000..c9f5a3b63 --- /dev/null +++ b/client/src/environments/environment.prod.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + apiUrl: '' +} diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts new file mode 100644 index 000000000..43d788541 --- /dev/null +++ b/client/src/environments/environment.ts @@ -0,0 +1,9 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `.angular-cli.json`. + +export const environment = { + production: false, + apiUrl: 'http://localhost:9000' +} diff --git a/client/src/index.html b/client/src/index.html index 4af6b12f6..f14323521 100644 --- a/client/src/index.html +++ b/client/src/index.html @@ -1,7 +1,7 @@ - <%= htmlWebpackPlugin.options.title %> + PeerTube @@ -14,9 +14,7 @@ - - - <%= htmlWebpackPlugin.files.webpackManifest %> + diff --git a/client/src/main.browser.aot.ts b/client/src/main.browser.aot.ts deleted file mode 100644 index 9635d615b..000000000 --- a/client/src/main.browser.aot.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* tslint: disable */ - -import { platformBrowser } from '@angular/platform-browser'; -import { decorateModuleRef } from './app/environment'; - -/** - * App Module - * our top level module that holds all of our components. - */ -import { AppModuleNgFactory } from '../compiled/src/app/app.module.ngfactory'; - -/** - * Bootstrap our Angular app with a top level NgModule. - */ -export function main(): Promise { - return platformBrowser() - .bootstrapModuleFactory(AppModuleNgFactory) - .then(decorateModuleRef) - .catch((err) => console.error(err)); -} - -switch (document.readyState) { - case 'loading': - document.addEventListener('DOMContentLoaded', _domReadyHandler, false); - break; - case 'interactive': - case 'complete': - default: - main(); -} - -function _domReadyHandler() { - document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); - main(); -} diff --git a/client/src/main.browser.ts b/client/src/main.browser.ts deleted file mode 100644 index 28f0d5781..000000000 --- a/client/src/main.browser.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* tslint: disable */ - -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' -import { decorateModuleRef } from './app/environment' -import { hmrModule } from '@angularclass/hmr' - -/** - * App Module - * our top level module that holds all of our components - */ -import { AppModule } from './app' - -/** - * Bootstrap our Angular app with a top level NgModule - */ -export function main (): Promise { - return platformBrowserDynamic() - .bootstrapModule(AppModule) - .then(decorateModuleRef) - .then((ngModuleRef: any) => { - // `module` global ref for webpackhmr - // Don't run this in Prod - return hmrModule(ngModuleRef, module) - }) - .catch((err) => console.error(err)) -} - -/** - * Needed for hmr - * in prod this is replace for document ready - */ -switch (document.readyState) { - case 'loading': - document.addEventListener('DOMContentLoaded', _domReadyHandler, false) - break - case 'interactive': - case 'complete': - default: - main() -} - -function _domReadyHandler () { - document.removeEventListener('DOMContentLoaded', _domReadyHandler, false) - main() -} diff --git a/client/src/main.ts b/client/src/main.ts new file mode 100644 index 000000000..91ec6da5f --- /dev/null +++ b/client/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); diff --git a/client/src/polyfills.browser.ts b/client/src/polyfills.browser.ts deleted file mode 100644 index b1a631655..000000000 --- a/client/src/polyfills.browser.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* tslint: disable */ - -// Polyfills -// (these modules are what are in 'angular2/bundles/angular2-polyfills' so don't use that here) - -require('intl'); -require('intl/locale-data/jsonp/en.js'); -import 'ie-shim'; // Internet Explorer - -// Prefer CoreJS over the polyfills above -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/set'; -import 'core-js/es6/weak-map'; -import 'core-js/es6/weak-set'; -import 'core-js/es6/typed'; -import 'core-js/es6/reflect'; -// see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709 -// import 'core-js/es6/promise'; - -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; - -if ('production' !== ENV) { - Error.stackTraceLimit = Infinity; - - require('zone.js/dist/long-stack-trace-zone'); -} diff --git a/client/src/polyfills.ts b/client/src/polyfills.ts new file mode 100644 index 000000000..d68672ffe --- /dev/null +++ b/client/src/polyfills.ts @@ -0,0 +1,66 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + + +/** + * Required to support Web Animations `@angular/platform-browser/animations`. + * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + + + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 9d347d566..08131406a 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -1,5 +1,12 @@ -$FontPathSourceSansPro: "../fonts/source-sans-pro"; -@import '~source-sans-pro/source-sans-pro'; +@import '_variables'; +@import '_mixins'; + +$icon-font-path: '../../node_modules/bootstrap-sass/assets/fonts/bootstrap/'; +@import '~bootstrap-sass/assets/stylesheets/_bootstrap'; + +$FontPathSourceSansPro: '../../node_modules/npm-font-source-sans-pro/fonts'; +@import '~npm-font-source-sans-pro/source-sans-pro'; + @import '~primeng/resources/themes/bootstrap/theme.css'; @import '~primeng/resources/primeng.css'; @import '~video.js/dist/video-js.css'; diff --git a/client/src/sass/_mixins.scss b/client/src/sass/include/_mixins.scss similarity index 98% rename from client/src/sass/_mixins.scss rename to client/src/sass/include/_mixins.scss index 2a7192fb2..f6f17846e 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/include/_mixins.scss @@ -1,3 +1,5 @@ +@import '_variables'; + @mixin disable-default-a-behaviour { &:hover, &:focus, &:active { text-decoration: none !important; diff --git a/client/src/sass/_variables.scss b/client/src/sass/include/_variables.scss similarity index 100% rename from client/src/sass/_variables.scss rename to client/src/sass/include/_variables.scss diff --git a/client/src/sass/pre-customizations.scss b/client/src/sass/pre-customizations.scss deleted file mode 100644 index 52eef50f2..000000000 --- a/client/src/sass/pre-customizations.scss +++ /dev/null @@ -1,877 +0,0 @@ -@import '_variables.scss'; -@import '_mixins.scss'; - -$bootstrap-sass-asset-helper: false !default; -// -// Variables -// -------------------------------------------------- - - -//== Colors -// -//## Gray and brand colors for use across Bootstrap. - -$gray-base: #000 !default; -$gray-darker: lighten($gray-base, 13.5%) !default; // #222 -$gray-dark: lighten($gray-base, 20%) !default; // #333 -$gray: lighten($gray-base, 33.5%) !default; // #555 -$gray-light: lighten($gray-base, 46.7%) !default; // #777 -$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee - -$brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 -$brand-success: #5cb85c !default; -$brand-info: #5bc0de !default; -$brand-warning: #f0ad4e !default; -$brand-danger: #d9534f !default; - - -//== Scaffolding -// -//## Settings for some of the most global styles. - -//** Background color for ``. -$body-bg: #fff !default; -//** Global text color on ``. -$text-color: $gray-dark !default; - -//** Global textual link color. -$link-color: $brand-primary !default; -//** Link hover color set via `darken()` function. -$link-hover-color: darken($link-color, 15%) !default; -//** Link hover decoration. -$link-hover-decoration: underline !default; - - -//== Typography -// -//## Font, line-height, and color for body text, headings, and more. - -$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; -$font-family-serif: Georgia, "Times New Roman", Times, serif !default; -//** Default monospace fonts for ``, ``, and `
`.
-$font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
-$font-family-base:        $font-family-sans-serif !default;
-
-$font-size-base:          14px !default;
-$font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
-$font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
-
-$font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
-$font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
-$font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
-$font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
-$font-size-h5:            $font-size-base !default;
-$font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
-
-//** Unit-less `line-height` for use in components like buttons.
-$line-height-base:        1.428571429 !default; // 20/14
-//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
-$line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
-
-//** By default, this inherits from the ``.
-$headings-font-family:    inherit !default;
-$headings-font-weight:    500 !default;
-$headings-line-height:    1.1 !default;
-$headings-color:          inherit !default;
-
-
-//== Iconography
-//
-//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
-
-//** Load fonts from this directory.
-
-// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
-// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
-$icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
-
-//** File name for all font files.
-$icon-font-name:          "glyphicons-halflings-regular" !default;
-//** Element ID within SVG icon file.
-$icon-font-svg-id:        "glyphicons_halflingsregular" !default;
-
-
-//== Components
-//
-//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
-
-$padding-base-vertical:     6px !default;
-$padding-base-horizontal:   12px !default;
-
-$padding-large-vertical:    10px !default;
-$padding-large-horizontal:  16px !default;
-
-$padding-small-vertical:    5px !default;
-$padding-small-horizontal:  10px !default;
-
-$padding-xs-vertical:       1px !default;
-$padding-xs-horizontal:     5px !default;
-
-$line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
-$line-height-small:         1.5 !default;
-
-$border-radius-base:        4px !default;
-$border-radius-large:       6px !default;
-$border-radius-small:       3px !default;
-
-//** Global color for active items (e.g., navs or dropdowns).
-$component-active-color:    #fff !default;
-//** Global background color for active items (e.g., navs or dropdowns).
-$component-active-bg:       $brand-primary !default;
-
-//** Width of the `border` for generating carets that indicator dropdowns.
-$caret-width-base:          4px !default;
-//** Carets increase slightly in size for larger components.
-$caret-width-large:         5px !default;
-
-
-//== Tables
-//
-//## Customizes the `.table` component with basic values, each used across all table variations.
-
-//** Padding for ``s and ``s.
-$table-cell-padding:            8px !default;
-//** Padding for cells in `.table-condensed`.
-$table-condensed-cell-padding:  5px !default;
-
-//** Default background color used for all tables.
-$table-bg:                      transparent !default;
-//** Background color used for `.table-striped`.
-$table-bg-accent:               #f9f9f9 !default;
-//** Background color used for `.table-hover`.
-$table-bg-hover:                #f5f5f5 !default;
-$table-bg-active:               $table-bg-hover !default;
-
-//** Border color for table and cell borders.
-$table-border-color:            #ddd !default;
-
-
-//== Buttons
-//
-//## For each of Bootstrap's buttons, define text, background and border color.
-
-$btn-font-weight:                normal !default;
-
-$btn-default-color:              #333 !default;
-$btn-default-bg:                 #fff !default;
-$btn-default-border:             #ccc !default;
-
-$btn-primary-color:              #fff !default;
-$btn-primary-bg:                 $brand-primary !default;
-$btn-primary-border:             darken($btn-primary-bg, 5%) !default;
-
-$btn-success-color:              #fff !default;
-$btn-success-bg:                 $brand-success !default;
-$btn-success-border:             darken($btn-success-bg, 5%) !default;
-
-$btn-info-color:                 #fff !default;
-$btn-info-bg:                    $brand-info !default;
-$btn-info-border:                darken($btn-info-bg, 5%) !default;
-
-$btn-warning-color:              #fff !default;
-$btn-warning-bg:                 $brand-warning !default;
-$btn-warning-border:             darken($btn-warning-bg, 5%) !default;
-
-$btn-danger-color:               #fff !default;
-$btn-danger-bg:                  $brand-danger !default;
-$btn-danger-border:              darken($btn-danger-bg, 5%) !default;
-
-$btn-link-disabled-color:        $gray-light !default;
-
-// Allows for customizing button radius independently from global border radius
-$btn-border-radius-base:         $border-radius-base !default;
-$btn-border-radius-large:        $border-radius-large !default;
-$btn-border-radius-small:        $border-radius-small !default;
-
-
-//== Forms
-//
-//##
-
-//** `` background color
-$input-bg:                       #fff !default;
-//** `` background color
-$input-bg-disabled:              $gray-lighter !default;
-
-//** Text color for ``s
-$input-color:                    $gray !default;
-//** `` border color
-$input-border:                   #ccc !default;
-
-// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
-//** Default `.form-control` border radius
-// This has no effect on ``s in CSS.
-$input-border-radius:            $border-radius-base !default;
-//** Large `.form-control` border radius
-$input-border-radius-large:      $border-radius-large !default;
-//** Small `.form-control` border radius
-$input-border-radius-small:      $border-radius-small !default;
-
-//** Border color for inputs on focus
-$input-border-focus:             #66afe9 !default;
-
-//** Placeholder text color
-$input-color-placeholder:        #999 !default;
-
-//** Default `.form-control` height
-$input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
-//** Large `.form-control` height
-$input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
-//** Small `.form-control` height
-$input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
-
-//** `.form-group` margin
-$form-group-margin-bottom:       15px !default;
-
-$legend-color:                   $gray-dark !default;
-$legend-border-color:            #e5e5e5 !default;
-
-//** Background color for textual input addons
-$input-group-addon-bg:           $gray-lighter !default;
-//** Border color for textual input addons
-$input-group-addon-border-color: $input-border !default;
-
-//** Disabled cursor for form controls and buttons.
-$cursor-disabled:                not-allowed !default;
-
-
-//== Dropdowns
-//
-//## Dropdown menu container and contents.
-
-//** Background for the dropdown menu.
-$dropdown-bg:                    #fff !default;
-//** Dropdown menu `border-color`.
-$dropdown-border:                rgba(0,0,0,.15) !default;
-//** Dropdown menu `border-color` **for IE8**.
-$dropdown-fallback-border:       #ccc !default;
-//** Divider color for between dropdown items.
-$dropdown-divider-bg:            #e5e5e5 !default;
-
-//** Dropdown link text color.
-$dropdown-link-color:            $gray-dark !default;
-//** Hover color for dropdown links.
-$dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
-//** Hover background for dropdown links.
-$dropdown-link-hover-bg:         #f5f5f5 !default;
-
-//** Active dropdown menu item text color.
-$dropdown-link-active-color:     $component-active-color !default;
-//** Active dropdown menu item background color.
-$dropdown-link-active-bg:        $component-active-bg !default;
-
-//** Disabled dropdown menu item background color.
-$dropdown-link-disabled-color:   $gray-light !default;
-
-//** Text color for headers within dropdown menus.
-$dropdown-header-color:          $gray-light !default;
-
-//** Deprecated `$dropdown-caret-color` as of v3.1.0
-$dropdown-caret-color:           #000 !default;
-
-
-//-- Z-index master list
-//
-// Warning: Avoid customizing these values. They're used for a bird's eye view
-// of components dependent on the z-axis and are designed to all work together.
-//
-// Note: These variables are not generated into the Customizer.
-
-$zindex-navbar:            1000 !default;
-$zindex-dropdown:          1000 !default;
-$zindex-popover:           1060 !default;
-$zindex-tooltip:           1070 !default;
-$zindex-navbar-fixed:      1030 !default;
-$zindex-modal-background:  1040 !default;
-$zindex-modal:             1050 !default;
-
-
-//== Media queries breakpoints
-//
-//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
-
-// Extra small screen / phone
-//** Deprecated `$screen-xs` as of v3.0.1
-$screen-xs:                  480px !default;
-//** Deprecated `$screen-xs-min` as of v3.2.0
-$screen-xs-min:              $screen-xs !default;
-//** Deprecated `$screen-phone` as of v3.0.1
-$screen-phone:               $screen-xs-min !default;
-
-// Small screen / tablet
-//** Deprecated `$screen-sm` as of v3.0.1
-$screen-sm:                  768px !default;
-$screen-sm-min:              $screen-sm !default;
-//** Deprecated `$screen-tablet` as of v3.0.1
-$screen-tablet:              $screen-sm-min !default;
-
-// Medium screen / desktop
-//** Deprecated `$screen-md` as of v3.0.1
-$screen-md:                  992px !default;
-$screen-md-min:              $screen-md !default;
-//** Deprecated `$screen-desktop` as of v3.0.1
-$screen-desktop:             $screen-md-min !default;
-
-// Large screen / wide desktop
-//** Deprecated `$screen-lg` as of v3.0.1
-$screen-lg:                  1200px !default;
-$screen-lg-min:              $screen-lg !default;
-//** Deprecated `$screen-lg-desktop` as of v3.0.1
-$screen-lg-desktop:          $screen-lg-min !default;
-
-// So media queries don't overlap when required, provide a maximum
-$screen-xs-max:              ($screen-sm-min - 1) !default;
-$screen-sm-max:              ($screen-md-min - 1) !default;
-$screen-md-max:              ($screen-lg-min - 1) !default;
-
-
-//== Grid system
-//
-//## Define your custom responsive grid.
-
-//** Number of columns in the grid.
-$grid-columns:              12 !default;
-//** Padding between columns. Gets divided in half for the left and right.
-$grid-gutter-width:         30px !default;
-// Navbar collapse
-//** Point at which the navbar becomes uncollapsed.
-$grid-float-breakpoint:     $screen-sm-min !default;
-//** Point at which the navbar begins collapsing.
-$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
-
-
-//== Container sizes
-//
-//## Define the maximum width of `.container` for different screen sizes.
-
-// Small screen / tablet
-$container-tablet:             (720px + $grid-gutter-width) !default;
-//** For `$screen-sm-min` and up.
-$container-sm:                 $container-tablet !default;
-
-// Medium screen / desktop
-$container-desktop:            (940px + $grid-gutter-width) !default;
-//** For `$screen-md-min` and up.
-$container-md:                 $container-desktop !default;
-
-// Large screen / wide desktop
-$container-large-desktop:      (1140px + $grid-gutter-width) !default;
-//** For `$screen-lg-min` and up.
-$container-lg:                 $container-large-desktop !default;
-
-
-//== Navbar
-//
-//##
-
-// Basics of a navbar
-$navbar-height:                    50px !default;
-$navbar-margin-bottom:             $line-height-computed !default;
-$navbar-border-radius:             $border-radius-base !default;
-$navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
-$navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
-$navbar-collapse-max-height:       340px !default;
-
-$navbar-default-color:             #777 !default;
-$navbar-default-bg:                #f8f8f8 !default;
-$navbar-default-border:            darken($navbar-default-bg, 6.5%) !default;
-
-// Navbar links
-$navbar-default-link-color:                #777 !default;
-$navbar-default-link-hover-color:          #333 !default;
-$navbar-default-link-hover-bg:             transparent !default;
-$navbar-default-link-active-color:         #555 !default;
-$navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%) !default;
-$navbar-default-link-disabled-color:       #ccc !default;
-$navbar-default-link-disabled-bg:          transparent !default;
-
-// Navbar brand label
-$navbar-default-brand-color:               $navbar-default-link-color !default;
-$navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%) !default;
-$navbar-default-brand-hover-bg:            transparent !default;
-
-// Navbar toggle
-$navbar-default-toggle-hover-bg:           #ddd !default;
-$navbar-default-toggle-icon-bar-bg:        #888 !default;
-$navbar-default-toggle-border-color:       #ddd !default;
-
-
-//=== Inverted navbar
-// Reset inverted navbar basics
-$navbar-inverse-color:                      lighten($gray-light, 15%) !default;
-$navbar-inverse-bg:                         #222 !default;
-$navbar-inverse-border:                     darken($navbar-inverse-bg, 10%) !default;
-
-// Inverted navbar links
-$navbar-inverse-link-color:                 lighten($gray-light, 15%) !default;
-$navbar-inverse-link-hover-color:           #fff !default;
-$navbar-inverse-link-hover-bg:              transparent !default;
-$navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color !default;
-$navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%) !default;
-$navbar-inverse-link-disabled-color:        #444 !default;
-$navbar-inverse-link-disabled-bg:           transparent !default;
-
-// Inverted navbar brand label
-$navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
-$navbar-inverse-brand-hover-color:          #fff !default;
-$navbar-inverse-brand-hover-bg:             transparent !default;
-
-// Inverted navbar toggle
-$navbar-inverse-toggle-hover-bg:            #333 !default;
-$navbar-inverse-toggle-icon-bar-bg:         #fff !default;
-$navbar-inverse-toggle-border-color:        #333 !default;
-
-
-//== Navs
-//
-//##
-
-//=== Shared nav styles
-$nav-link-padding:                          10px 15px !default;
-$nav-link-hover-bg:                         $gray-lighter !default;
-
-$nav-disabled-link-color:                   $gray-light !default;
-$nav-disabled-link-hover-color:             $gray-light !default;
-
-//== Tabs
-$nav-tabs-border-color:                     #ddd !default;
-
-$nav-tabs-link-hover-border-color:          $gray-lighter !default;
-
-$nav-tabs-active-link-hover-bg:             $body-bg !default;
-$nav-tabs-active-link-hover-color:          $gray !default;
-$nav-tabs-active-link-hover-border-color:   #ddd !default;
-
-$nav-tabs-justified-link-border-color:            #ddd !default;
-$nav-tabs-justified-active-link-border-color:     $body-bg !default;
-
-//== Pills
-$nav-pills-border-radius:                   $border-radius-base !default;
-$nav-pills-active-link-hover-bg:            $component-active-bg !default;
-$nav-pills-active-link-hover-color:         $component-active-color !default;
-
-
-//== Pagination
-//
-//##
-
-$pagination-color:                     $link-color !default;
-$pagination-bg:                        #fff !default;
-$pagination-border:                    #ddd !default;
-
-$pagination-hover-color:               $link-hover-color !default;
-$pagination-hover-bg:                  $gray-lighter !default;
-$pagination-hover-border:              #ddd !default;
-
-$pagination-active-color:              #fff !default;
-$pagination-active-bg:                 $brand-primary !default;
-$pagination-active-border:             $brand-primary !default;
-
-$pagination-disabled-color:            $gray-light !default;
-$pagination-disabled-bg:               #fff !default;
-$pagination-disabled-border:           #ddd !default;
-
-
-//== Pager
-//
-//##
-
-$pager-bg:                             $pagination-bg !default;
-$pager-border:                         $pagination-border !default;
-$pager-border-radius:                  15px !default;
-
-$pager-hover-bg:                       $pagination-hover-bg !default;
-
-$pager-active-bg:                      $pagination-active-bg !default;
-$pager-active-color:                   $pagination-active-color !default;
-
-$pager-disabled-color:                 $pagination-disabled-color !default;
-
-
-//== Jumbotron
-//
-//##
-
-$jumbotron-padding:              30px !default;
-$jumbotron-color:                inherit !default;
-$jumbotron-bg:                   $gray-lighter !default;
-$jumbotron-heading-color:        inherit !default;
-$jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
-$jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
-
-
-//== Form states and alerts
-//
-//## Define colors for form feedback states and, by default, alerts.
-
-$state-success-text:             #3c763d !default;
-$state-success-bg:               #dff0d8 !default;
-$state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%) !default;
-
-$state-info-text:                #31708f !default;
-$state-info-bg:                  #d9edf7 !default;
-$state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%) !default;
-
-$state-warning-text:             #8a6d3b !default;
-$state-warning-bg:               #fcf8e3 !default;
-$state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%) !default;
-
-$state-danger-text:              #a94442 !default;
-$state-danger-bg:                #f2dede !default;
-$state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%) !default;
-
-
-//== Tooltips
-//
-//##
-
-//** Tooltip max width
-$tooltip-max-width:           200px !default;
-//** Tooltip text color
-$tooltip-color:               #fff !default;
-//** Tooltip background color
-$tooltip-bg:                  #000 !default;
-$tooltip-opacity:             .9 !default;
-
-//** Tooltip arrow width
-$tooltip-arrow-width:         5px !default;
-//** Tooltip arrow color
-$tooltip-arrow-color:         $tooltip-bg !default;
-
-
-//== Popovers
-//
-//##
-
-//** Popover body background color
-$popover-bg:                          #fff !default;
-//** Popover maximum width
-$popover-max-width:                   276px !default;
-//** Popover border color
-$popover-border-color:                rgba(0,0,0,.2) !default;
-//** Popover fallback border color
-$popover-fallback-border-color:       #ccc !default;
-
-//** Popover title background color
-$popover-title-bg:                    darken($popover-bg, 3%) !default;
-
-//** Popover arrow width
-$popover-arrow-width:                 10px !default;
-//** Popover arrow color
-$popover-arrow-color:                 $popover-bg !default;
-
-//** Popover outer arrow width
-$popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
-//** Popover outer arrow color
-$popover-arrow-outer-color:           fade_in($popover-border-color, 0.05) !default;
-//** Popover outer arrow fallback color
-$popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
-
-
-//== Labels
-//
-//##
-
-//** Default label background color
-$label-default-bg:            $gray-light !default;
-//** Primary label background color
-$label-primary-bg:            $brand-primary !default;
-//** Success label background color
-$label-success-bg:            $brand-success !default;
-//** Info label background color
-$label-info-bg:               $brand-info !default;
-//** Warning label background color
-$label-warning-bg:            $brand-warning !default;
-//** Danger label background color
-$label-danger-bg:             $brand-danger !default;
-
-//** Default label text color
-$label-color:                 #fff !default;
-//** Default text color of a linked label
-$label-link-hover-color:      #fff !default;
-
-
-//== Modals
-//
-//##
-
-//** Padding applied to the modal body
-$modal-inner-padding:         15px !default;
-
-//** Padding applied to the modal title
-$modal-title-padding:         15px !default;
-//** Modal title line-height
-$modal-title-line-height:     $line-height-base !default;
-
-//** Background color of modal content area
-$modal-content-bg:                             #fff !default;
-//** Modal content border color
-$modal-content-border-color:                   rgba(0,0,0,.2) !default;
-//** Modal content border color **for IE8**
-$modal-content-fallback-border-color:          #999 !default;
-
-//** Modal backdrop background color
-$modal-backdrop-bg:           #000 !default;
-//** Modal backdrop opacity
-$modal-backdrop-opacity:      .5 !default;
-//** Modal header border color
-$modal-header-border-color:   #e5e5e5 !default;
-//** Modal footer border color
-$modal-footer-border-color:   $modal-header-border-color !default;
-
-$modal-lg:                    900px !default;
-$modal-md:                    600px !default;
-$modal-sm:                    300px !default;
-
-
-//== Alerts
-//
-//## Define alert colors, border radius, and padding.
-
-$alert-padding:               15px !default;
-$alert-border-radius:         $border-radius-base !default;
-$alert-link-font-weight:      bold !default;
-
-$alert-success-bg:            $state-success-bg !default;
-$alert-success-text:          $state-success-text !default;
-$alert-success-border:        $state-success-border !default;
-
-$alert-info-bg:               $state-info-bg !default;
-$alert-info-text:             $state-info-text !default;
-$alert-info-border:           $state-info-border !default;
-
-$alert-warning-bg:            $state-warning-bg !default;
-$alert-warning-text:          $state-warning-text !default;
-$alert-warning-border:        $state-warning-border !default;
-
-$alert-danger-bg:             $state-danger-bg !default;
-$alert-danger-text:           $state-danger-text !default;
-$alert-danger-border:         $state-danger-border !default;
-
-
-//== Progress bars
-//
-//##
-
-//** Background color of the whole progress component
-$progress-bg:                 #f5f5f5 !default;
-//** Progress bar text color
-$progress-bar-color:          #fff !default;
-//** Variable for setting rounded corners on progress bar.
-$progress-border-radius:      $border-radius-base !default;
-
-//** Default progress bar color
-$progress-bar-bg:             $brand-primary !default;
-//** Success progress bar color
-$progress-bar-success-bg:     $brand-success !default;
-//** Warning progress bar color
-$progress-bar-warning-bg:     $brand-warning !default;
-//** Danger progress bar color
-$progress-bar-danger-bg:      $brand-danger !default;
-//** Info progress bar color
-$progress-bar-info-bg:        $brand-info !default;
-
-
-//== List group
-//
-//##
-
-//** Background color on `.list-group-item`
-$list-group-bg:                 #fff !default;
-//** `.list-group-item` border color
-$list-group-border:             #ddd !default;
-//** List group border radius
-$list-group-border-radius:      $border-radius-base !default;
-
-//** Background color of single list items on hover
-$list-group-hover-bg:           #f5f5f5 !default;
-//** Text color of active list items
-$list-group-active-color:       $component-active-color !default;
-//** Background color of active list items
-$list-group-active-bg:          $component-active-bg !default;
-//** Border color of active list elements
-$list-group-active-border:      $list-group-active-bg !default;
-//** Text color for content within active list items
-$list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
-
-//** Text color of disabled list items
-$list-group-disabled-color:      $gray-light !default;
-//** Background color of disabled list items
-$list-group-disabled-bg:         $gray-lighter !default;
-//** Text color for content within disabled list items
-$list-group-disabled-text-color: $list-group-disabled-color !default;
-
-$list-group-link-color:         #555 !default;
-$list-group-link-hover-color:   $list-group-link-color !default;
-$list-group-link-heading-color: #333 !default;
-
-
-//== Panels
-//
-//##
-
-$panel-bg:                    #fff !default;
-$panel-body-padding:          15px !default;
-$panel-heading-padding:       10px 15px !default;
-$panel-footer-padding:        $panel-heading-padding !default;
-$panel-border-radius:         $border-radius-base !default;
-
-//** Border color for elements within panels
-$panel-inner-border:          #ddd !default;
-$panel-footer-bg:             #f5f5f5 !default;
-
-$panel-default-text:          $gray-dark !default;
-$panel-default-border:        #ddd !default;
-$panel-default-heading-bg:    #f5f5f5 !default;
-
-$panel-primary-text:          #fff !default;
-$panel-primary-border:        $brand-primary !default;
-$panel-primary-heading-bg:    $brand-primary !default;
-
-$panel-success-text:          $state-success-text !default;
-$panel-success-border:        $state-success-border !default;
-$panel-success-heading-bg:    $state-success-bg !default;
-
-$panel-info-text:             $state-info-text !default;
-$panel-info-border:           $state-info-border !default;
-$panel-info-heading-bg:       $state-info-bg !default;
-
-$panel-warning-text:          $state-warning-text !default;
-$panel-warning-border:        $state-warning-border !default;
-$panel-warning-heading-bg:    $state-warning-bg !default;
-
-$panel-danger-text:           $state-danger-text !default;
-$panel-danger-border:         $state-danger-border !default;
-$panel-danger-heading-bg:     $state-danger-bg !default;
-
-
-//== Thumbnails
-//
-//##
-
-//** Padding around the thumbnail image
-$thumbnail-padding:           4px !default;
-//** Thumbnail background color
-$thumbnail-bg:                $body-bg !default;
-//** Thumbnail border color
-$thumbnail-border:            #ddd !default;
-//** Thumbnail border radius
-$thumbnail-border-radius:     $border-radius-base !default;
-
-//** Custom text color for thumbnail captions
-$thumbnail-caption-color:     $text-color !default;
-//** Padding around the thumbnail caption
-$thumbnail-caption-padding:   9px !default;
-
-
-//== Wells
-//
-//##
-
-$well-bg:                     #f5f5f5 !default;
-$well-border:                 darken($well-bg, 7%) !default;
-
-
-//== Badges
-//
-//##
-
-$badge-color:                 #fff !default;
-//** Linked badge text color on hover
-$badge-link-hover-color:      #fff !default;
-$badge-bg:                    $gray-light !default;
-
-//** Badge text color in active nav link
-$badge-active-color:          $link-color !default;
-//** Badge background color in active nav link
-$badge-active-bg:             #fff !default;
-
-$badge-font-weight:           bold !default;
-$badge-line-height:           1 !default;
-$badge-border-radius:         10px !default;
-
-
-//== Breadcrumbs
-//
-//##
-
-$breadcrumb-padding-vertical:   8px !default;
-$breadcrumb-padding-horizontal: 15px !default;
-//** Breadcrumb background color
-$breadcrumb-bg:                 #f5f5f5 !default;
-//** Breadcrumb text color
-$breadcrumb-color:              #ccc !default;
-//** Text color of current page in the breadcrumb
-$breadcrumb-active-color:       $gray-light !default;
-//** Textual separator for between breadcrumb elements
-$breadcrumb-separator:          "/" !default;
-
-
-//== Carousel
-//
-//##
-
-$carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
-
-$carousel-control-color:                      #fff !default;
-$carousel-control-width:                      15% !default;
-$carousel-control-opacity:                    .5 !default;
-$carousel-control-font-size:                  20px !default;
-
-$carousel-indicator-active-bg:                #fff !default;
-$carousel-indicator-border-color:             #fff !default;
-
-$carousel-caption-color:                      #fff !default;
-
-
-//== Close
-//
-//##
-
-$close-font-weight:           bold !default;
-$close-color:                 #000 !default;
-$close-text-shadow:           0 1px 0 #fff !default;
-
-
-//== Code
-//
-//##
-
-$code-color:                  #c7254e !default;
-$code-bg:                     #f9f2f4 !default;
-
-$kbd-color:                   #fff !default;
-$kbd-bg:                      #333 !default;
-
-$pre-bg:                      #f5f5f5 !default;
-$pre-color:                   $gray-dark !default;
-$pre-border-color:            #ccc !default;
-$pre-scrollable-max-height:   340px !default;
-
-
-//== Type
-//
-//##
-
-//** Horizontal offset for forms and lists.
-$component-offset-horizontal: 180px !default;
-//** Text muted color
-$text-muted:                  $gray-light !default;
-//** Abbreviations and acronyms border color
-$abbr-border-color:           $gray-light !default;
-//** Headings small color
-$headings-small-color:        $gray-light !default;
-//** Blockquote small color
-$blockquote-small-color:      $gray-light !default;
-//** Blockquote font size
-$blockquote-font-size:        ($font-size-base * 1.25) !default;
-//** Blockquote border color
-$blockquote-border-color:     $gray-lighter !default;
-//** Page header border color
-$page-header-border-color:    $gray-lighter !default;
-//** Width of horizontal description list titles
-$dl-horizontal-offset:        $component-offset-horizontal !default;
-//** Point at which .dl-horizontal becomes horizontal
-$dl-horizontal-breakpoint:    $grid-float-breakpoint !default;
-//** Horizontal line color.
-$hr-border:                   $gray-lighter !default;
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index f696df968..40153a51b 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -1,6 +1,6 @@
 import './embed.scss'
 
-import videojs from 'video.js'
+import * as videojs from 'video.js'
 import '../../assets/player/peertube-videojs-plugin'
 import 'videojs-dock/dist/videojs-dock.es.js'
 import { VideoDetails } from '../../../../shared'
diff --git a/client/src/tsconfig.app.json b/client/src/tsconfig.app.json
new file mode 100644
index 000000000..39ba8dbac
--- /dev/null
+++ b/client/src/tsconfig.app.json
@@ -0,0 +1,13 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/app",
+    "baseUrl": "./",
+    "module": "es2015",
+    "types": []
+  },
+  "exclude": [
+    "test.ts",
+    "**/*.spec.ts"
+  ]
+}
diff --git a/client/src/typings.d.ts b/client/src/typings.d.ts
new file mode 100644
index 000000000..ef5c7bd62
--- /dev/null
+++ b/client/src/typings.d.ts
@@ -0,0 +1,5 @@
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+  id: string;
+}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 66c7bb444..a6c016bf3 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -1,32 +1,19 @@
 {
+  "compileOnSave": false,
   "compilerOptions": {
-    "target": "es5",
-    "module": "commonjs",
+    "outDir": "./dist/out-tsc",
+    "sourceMap": true,
+    "declaration": false,
     "moduleResolution": "node",
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
-    "allowSyntheticDefaultImports": true,
-    "sourceMap": true,
-    "noEmitHelpers": true,
-    "importHelpers": true,
-    "strictNullChecks": false,
-    "baseUrl": "./src",
-    "paths": [
+    "target": "es5",
+    "typeRoots": [
+      "node_modules/@types"
     ],
     "lib": [
-      "dom",
-      "es6"
+      "es2017",
+      "dom"
     ]
-  },
-  "exclude": [
-    "node_modules",
-    "dist"
-  ],
-  "awesomeTypescriptLoaderOptions": {
-    "forkChecker": true,
-    "useWebpackText": true
-  },
-  "compileOnSave": false,
-  "buildOnSave": false,
-  "atom": { "rewriteTsconfig": false }
+  }
 }
diff --git a/client/tsconfig.webpack.json b/client/tsconfig.webpack.json
deleted file mode 100644
index 63532c323..000000000
--- a/client/tsconfig.webpack.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "es5",
-    "module": "es2015",
-    "moduleResolution": "node",
-    "emitDecoratorMetadata": true,
-    "experimentalDecorators": true,
-    "allowSyntheticDefaultImports": true,
-    "sourceMap": true,
-    "noEmitHelpers": true,
-    "importHelpers": true,
-    "strictNullChecks": false,
-    "baseUrl": "./src",
-    "outDir": "./dist",
-    "paths": [
-    ],
-    "lib": [
-      "dom",
-      "es6"
-    ]
-  },
-  "exclude": [
-    "node_modules",
-    "dist"
-  ],
-  "awesomeTypescriptLoaderOptions": {
-    "forkChecker": true,
-    "useWebpackText": true
-  },
-  "angularCompilerOptions": {
-    "genDir": "./compiled",
-    "skipMetadataEmit": true
-  },
-  "compileOnSave": false,
-  "buildOnSave": false,
-  "atom": { "rewriteTsconfig": false }
-}
diff --git a/client/yarn.lock b/client/yarn.lock
index 9cead52e4..f6fd91f71 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -2,12 +2,101 @@
 # yarn lockfile v1
 
 
+"@angular-devkit/build-optimizer@~0.0.35":
+  version "0.0.35"
+  resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.35.tgz#3aadad1d7e9ffc7dcd106fda8a5670465936562c"
+  dependencies:
+    loader-utils "^1.1.0"
+    source-map "^0.5.6"
+    typescript "~2.6.1"
+    webpack-sources "^1.0.1"
+
+"@angular-devkit/core@0.0.22":
+  version "0.0.22"
+  resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.22.tgz#e90f46bf7ff47d260a767959267bc65ffee39ef1"
+  dependencies:
+    source-map "^0.5.6"
+
+"@angular-devkit/schematics@~0.0.40":
+  version "0.0.41"
+  resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.41.tgz#9a066e442bdf10de4a093d6dd33a58e600d1b101"
+  dependencies:
+    "@angular-devkit/core" "0.0.22"
+    "@ngtools/json-schema" "^1.1.0"
+    "@schematics/schematics" "0.0.10"
+    minimist "^1.2.0"
+    rxjs "^5.5.2"
+
 "@angular/animations@~4.4.0":
   version "4.4.6"
   resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.4.6.tgz#fa661899a8a4e38cb7c583c7a5c97ce65d592a35"
   dependencies:
     tslib "^1.7.1"
 
+"@angular/cli@^1.6.0":
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.6.0.tgz#eba521f6a8e4c2db628300baddbc4da13ca96998"
+  dependencies:
+    "@angular-devkit/build-optimizer" "~0.0.35"
+    "@angular-devkit/schematics" "~0.0.40"
+    "@ngtools/json-schema" "1.1.0"
+    "@ngtools/webpack" "1.9.0"
+    "@schematics/angular" "~0.1.10"
+    autoprefixer "^6.5.3"
+    chalk "~2.2.0"
+    circular-dependency-plugin "^4.2.1"
+    common-tags "^1.3.1"
+    copy-webpack-plugin "^4.1.1"
+    core-object "^3.1.0"
+    css-loader "^0.28.1"
+    cssnano "^3.10.0"
+    denodeify "^1.2.1"
+    ember-cli-string-utils "^1.0.0"
+    exports-loader "^0.6.3"
+    extract-text-webpack-plugin "^3.0.2"
+    file-loader "^1.1.5"
+    fs-extra "^4.0.0"
+    glob "^7.0.3"
+    html-webpack-plugin "^2.29.0"
+    istanbul-instrumenter-loader "^2.0.0"
+    karma-source-map-support "^1.2.0"
+    less "^2.7.2"
+    less-loader "^4.0.5"
+    license-webpack-plugin "^1.0.0"
+    lodash "^4.11.1"
+    memory-fs "^0.4.1"
+    minimatch "^3.0.4"
+    node-modules-path "^1.0.0"
+    nopt "^4.0.1"
+    opn "~5.1.0"
+    portfinder "~1.0.12"
+    postcss-custom-properties "^6.1.0"
+    postcss-loader "^2.0.8"
+    postcss-url "^7.1.2"
+    raw-loader "^0.5.1"
+    resolve "^1.1.7"
+    rxjs "^5.5.2"
+    sass-loader "^6.0.3"
+    semver "^5.1.0"
+    silent-error "^1.0.0"
+    source-map-loader "^0.2.0"
+    source-map-support "^0.4.1"
+    style-loader "^0.13.1"
+    stylus "^0.54.5"
+    stylus-loader "^3.0.1"
+    uglifyjs-webpack-plugin "~1.1.2"
+    url-loader "^0.6.2"
+    webpack "~3.10.0"
+    webpack-concat-plugin "^1.4.2"
+    webpack-dev-middleware "~1.12.0"
+    webpack-dev-server "~2.9.3"
+    webpack-merge "^4.1.0"
+    webpack-sources "^1.0.0"
+    webpack-subresource-integrity "^1.0.1"
+    zone.js "^0.8.14"
+  optionalDependencies:
+    node-sass "^4.3.0"
+
 "@angular/common@~4.4.0":
   version "4.4.6"
   resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.4.6.tgz#4b81420724e0828a0e839b95a55eb1a7e83918f2"
@@ -46,6 +135,10 @@
   dependencies:
     tslib "^1.7.1"
 
+"@angular/language-service@^5.1.0":
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.1.0.tgz#0cdd1a9d6056f1567bfe3c016352bc2e9d20cb84"
+
 "@angular/platform-browser-dynamic@~4.4.0":
   version "4.4.6"
   resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.4.6.tgz#4d3d9a6a7bf2cf3de4058a615ae059eff641fa36"
@@ -80,12 +173,38 @@
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/@angularclass/hmr/-/hmr-2.1.3.tgz#34e658ed3da37f23b0a200e2da5a89be92bb209f"
 
+"@ngtools/json-schema@1.1.0", "@ngtools/json-schema@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@ngtools/json-schema/-/json-schema-1.1.0.tgz#c3a0c544d62392acc2813a42c8a0dc6f58f86922"
+
+"@ngtools/webpack@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.9.0.tgz#ef395c45be2de9beb93a2b9f5f171d28c344eb10"
+  dependencies:
+    chalk "~2.2.0"
+    enhanced-resolve "^3.1.0"
+    loader-utils "^1.0.2"
+    magic-string "^0.22.3"
+    semver "^5.3.0"
+    source-map "^0.5.6"
+    tree-kill "^1.0.0"
+
 "@ngx-meta/core@^4.0.1":
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/@ngx-meta/core/-/core-4.0.1.tgz#b035febeeb92876920480f70719fcf953dc0245f"
   dependencies:
     tslib "~1.7.1"
 
+"@schematics/angular@~0.1.10":
+  version "0.1.10"
+  resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.10.tgz#654d867992277484ed2d82d015b2d3b6ff87b93d"
+  dependencies:
+    "@angular-devkit/core" "0.0.22"
+
+"@schematics/schematics@0.0.10":
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/@schematics/schematics/-/schematics-0.0.10.tgz#b27b49a1a5482dc6c1c93fb3c20371c95874938b"
+
 "@types/bittorrent-protocol@*":
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/@types/bittorrent-protocol/-/bittorrent-protocol-2.2.2.tgz#169e9633e1bd18e6b830d11cf42e611b1972cb83"
@@ -398,6 +517,10 @@ arrify@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
 
+asap@~2.0.3:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+
 asn1.js@^4.0.0:
   version "4.9.2"
   resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.2.tgz#8117ef4f7ed87cd8f89044b5bff97ac243a16c9a"
@@ -472,7 +595,7 @@ atob@~1.1.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773"
 
-autoprefixer@^6.3.1:
+autoprefixer@^6.3.1, autoprefixer@^6.5.3:
   version "6.7.7"
   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
   dependencies:
@@ -540,7 +663,7 @@ babel-core@^6.25.0, babel-core@^6.26.0:
     slash "^1.0.0"
     source-map "^0.5.6"
 
-babel-generator@^6.26.0:
+babel-generator@^6.18.0, babel-generator@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
   dependencies:
@@ -937,7 +1060,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runti
     core-js "^2.4.0"
     regenerator-runtime "^0.11.0"
 
-babel-template@^6.24.1, babel-template@^6.26.0:
+babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
   dependencies:
@@ -947,7 +1070,7 @@ babel-template@^6.24.1, babel-template@^6.26.0:
     babylon "^6.18.0"
     lodash "^4.17.4"
 
-babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
   dependencies:
@@ -961,7 +1084,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0:
     invariant "^2.2.2"
     lodash "^4.17.4"
 
-babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
+babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
   dependencies:
@@ -1176,27 +1299,10 @@ boom@5.x.x:
   dependencies:
     hoek "4.x.x"
 
-bootstrap-loader@2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/bootstrap-loader/-/bootstrap-loader-2.2.0.tgz#dc162c82497275f0e427ab49bc406b4e6a8e250b"
-  dependencies:
-    chalk "^1.1.3"
-    escape-regexp "0.0.1"
-    exports-loader "^0.6.3"
-    js-yaml "^3.7.0"
-    loader-utils "^1.0.2"
-    resolve "^1.1.7"
-    semver "^5.3.0"
-    strip-json-comments "^2.0.1"
-
-bootstrap-sass@^3.3.6:
+bootstrap-sass@^3.3.7:
   version "3.3.7"
   resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.7.tgz#6596c7ab40f6637393323ab0bc80d064fc630498"
 
-bootstrap@^3.3.6:
-  version "3.3.7"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71"
-
 brace-expansion@^1.1.7:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@@ -1442,6 +1548,10 @@ cardinal@^1.0.0:
     ansicolors "~0.2.1"
     redeyed "~1.0.0"
 
+caseless@~0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
+
 caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -1471,6 +1581,18 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0:
     escape-string-regexp "^1.0.5"
     supports-color "^4.0.0"
 
+chalk@~2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.2.2.tgz#4403f5cf18f35c05f51fbdf152bf588f956cf7cb"
+  dependencies:
+    ansi-styles "^3.1.0"
+    escape-string-regexp "^1.0.5"
+    supports-color "^4.0.0"
+
+charenc@~0.0.1:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+
 chokidar@^1.6.0, chokidar@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
@@ -1505,6 +1627,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
     inherits "^2.0.1"
     safe-buffer "^5.0.1"
 
+circular-dependency-plugin@^4.2.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-4.3.0.tgz#2a12824e584546e1aeea5865b7bf234a11c4a695"
+
 circular-json@^0.3.1:
   version "0.3.3"
   resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
@@ -1583,6 +1709,10 @@ clone@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
 
+clone@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
+
 closest-to@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/closest-to/-/closest-to-2.0.0.tgz#bb2a860edb7769b62d04821748ae50da24dbefaa"
@@ -1669,6 +1799,16 @@ commander@2.11.x, commander@^2.9.0, commander@~2.11.0:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
 
+commander@~2.12.1:
+  version "2.12.2"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555"
+
+common-tags@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.5.1.tgz#e2e39931a013cd02253defeed89a1ad615a27f07"
+  dependencies:
+    babel-runtime "^6.26.0"
+
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -1751,6 +1891,10 @@ convert-source-map@^1.1.1, convert-source-map@^1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
 
+convert-source-map@^1.3.0:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
+
 cookie-signature@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -1787,14 +1931,43 @@ copy-webpack-plugin@^4.0.0:
     minimatch "^3.0.4"
     node-dir "^0.1.10"
 
+copy-webpack-plugin@^4.1.1:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.3.tgz#4a3c61089f3b635777f0f0af346c338b39d63755"
+  dependencies:
+    bluebird "^3.5.1"
+    glob "^7.1.2"
+    is-glob "^4.0.0"
+    loader-utils "^0.2.15"
+    lodash "^4.3.0"
+    minimatch "^3.0.4"
+
 core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
 
+core-object@^3.1.0:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/core-object/-/core-object-3.1.5.tgz#fa627b87502adc98045e44678e9a8ec3b9c0d2a9"
+  dependencies:
+    chalk "^2.0.0"
+
 core-util-is@1.0.2, core-util-is@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
 
+cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892"
+  dependencies:
+    is-directory "^0.3.1"
+    js-yaml "^3.4.3"
+    minimist "^1.2.0"
+    object-assign "^4.1.0"
+    os-homedir "^1.0.1"
+    parse-json "^2.2.0"
+    require-from-string "^1.1.0"
+
 create-ecdh@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
@@ -1856,6 +2029,10 @@ cross-spawn@^5.0.1:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
+crypt@~0.0.1:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
+
 cryptiles@2.x.x:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@@ -1888,7 +2065,7 @@ css-color-names@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
 
-css-loader@^0.28.4:
+css-loader@^0.28.1, css-loader@^0.28.4:
   version "0.28.7"
   resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.7.tgz#5f2ee989dd32edd907717f953317656160999c1b"
   dependencies:
@@ -1907,6 +2084,10 @@ css-loader@^0.28.4:
     postcss-value-parser "^3.3.0"
     source-list-map "^2.0.0"
 
+css-parse@1.7.x:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b"
+
 css-select@^1.1.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
@@ -1953,7 +2134,7 @@ cssesc@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
 
-"cssnano@>=2.6.1 <4":
+"cssnano@>=2.6.1 <4", cssnano@^3.10.0:
   version "3.10.0"
   resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
   dependencies:
@@ -1997,6 +2178,10 @@ csso@~2.3.1:
     clap "^1.0.9"
     source-map "^0.5.3"
 
+cuint@latest:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
+
 currently-unhandled@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -2027,15 +2212,15 @@ debug-log@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f"
 
-debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.5.2, debug@^2.6.6, debug@^2.6.8:
-  version "2.6.9"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+debug@*, debug@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
   dependencies:
     ms "2.0.0"
 
-debug@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.5.2, debug@^2.6.6, debug@^2.6.8:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   dependencies:
     ms "2.0.0"
 
@@ -2130,6 +2315,10 @@ delegates@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
 
+denodeify@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
+
 depd@1.1.1, depd@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
@@ -2280,7 +2469,7 @@ ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
-ejs@^2.5.6:
+ejs@^2.5.6, ejs@^2.5.7:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
 
@@ -2300,6 +2489,10 @@ elliptic@^6.0.0:
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.0"
 
+ember-cli-string-utils@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/ember-cli-string-utils/-/ember-cli-string-utils-1.1.0.tgz#39b677fc2805f55173735376fcef278eaa4452a1"
+
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@@ -2323,7 +2516,7 @@ enhanced-resolve@3.3.0:
     object-assign "^4.0.1"
     tapable "^0.2.5"
 
-enhanced-resolve@^3.4.0:
+enhanced-resolve@^3.1.0, enhanced-resolve@^3.4.0:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
   dependencies:
@@ -2336,6 +2529,12 @@ entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
+errno@^0.1.1:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.5.tgz#a563781a6052bc2c9ccd89e8cef0eb9506e0c321"
+  dependencies:
+    prr "~1.0.1"
+
 errno@^0.1.3, errno@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -2430,10 +2629,6 @@ escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
 
-escape-regexp@0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/escape-regexp/-/escape-regexp-0.0.1.tgz#f44bda12d45bbdf9cb7f862ee7e4827b3dd32254"
-
 escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -2688,7 +2883,7 @@ exports-loader@^0.6.3:
     loader-utils "^1.0.2"
     source-map "0.5.x"
 
-express@^4.13.3, express@^4.15.2:
+express@^4.13.3, express@^4.15.2, express@^4.16.2:
   version "4.16.2"
   resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
   dependencies:
@@ -2752,7 +2947,7 @@ extglob@^2.0.2:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-extract-text-webpack-plugin@^3.0.0:
+extract-text-webpack-plugin@^3.0.0, extract-text-webpack-plugin@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7"
   dependencies:
@@ -2989,6 +3184,14 @@ fs-chunk-store@^1.6.2:
     run-parallel "^1.1.2"
     thunky "^1.0.1"
 
+fs-extra@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
 fs-extra@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b"
@@ -3114,6 +3317,27 @@ glob-parent@^2.0.0:
   dependencies:
     is-glob "^2.0.0"
 
+glob@7.0.x:
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.2"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+glob@^6.0.4:
+  version "6.0.4"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
+  dependencies:
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "2 || 3"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
 glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@@ -3198,6 +3422,15 @@ har-schema@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
 
+har-validator@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
+  dependencies:
+    chalk "^1.1.1"
+    commander "^2.9.0"
+    is-my-json-valid "^2.12.4"
+    pinkie-promise "^2.0.0"
+
 har-validator@~4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
@@ -3368,7 +3601,7 @@ html-minifier@^3.2.3:
     relateurl "0.2.x"
     uglify-js "3.1.x"
 
-html-webpack-plugin@^2.19.0:
+html-webpack-plugin@^2.19.0, html-webpack-plugin@^2.29.0:
   version "2.30.1"
   resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5"
   dependencies:
@@ -3471,6 +3704,10 @@ ignore@^3.0.11, ignore@^3.0.9, ignore@^3.2.0:
   version "3.3.7"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
 
+image-size@~0.5.0:
+  version "0.5.5"
+  resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
+
 immediate-chunk-store@^1.0.8:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/immediate-chunk-store/-/immediate-chunk-store-1.0.8.tgz#0ecdad0c546332672d7b5b511b26bb18ce56e73f"
@@ -3617,7 +3854,7 @@ is-binary-path@^1.0.0:
   dependencies:
     binary-extensions "^1.0.0"
 
-is-buffer@^1.0.2, is-buffer@^1.1.5:
+is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
 
@@ -3657,6 +3894,10 @@ is-descriptor@^1.0.0:
     is-data-descriptor "^0.1.4"
     kind-of "^5.0.0"
 
+is-directory@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+
 is-dotfile@^1.0.0:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
@@ -3721,7 +3962,7 @@ is-glob@^4.0.0:
   dependencies:
     is-extglob "^2.1.1"
 
-is-my-json-valid@^2.10.0:
+is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
   version "2.16.1"
   resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11"
   dependencies:
@@ -3850,6 +4091,31 @@ isstream@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
 
+istanbul-instrumenter-loader@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz#e5492900ab0bba835efa8024cb00be9b3eea2700"
+  dependencies:
+    convert-source-map "^1.3.0"
+    istanbul-lib-instrument "^1.1.3"
+    loader-utils "^0.2.16"
+    object-assign "^4.1.0"
+
+istanbul-lib-coverage@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
+
+istanbul-lib-instrument@^1.1.3:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e"
+  dependencies:
+    babel-generator "^6.18.0"
+    babel-template "^6.16.0"
+    babel-traverse "^6.18.0"
+    babel-types "^6.18.0"
+    babylon "^6.18.0"
+    istanbul-lib-coverage "^1.1.1"
+    semver "^5.3.0"
+
 js-base64@^2.1.8, js-base64@^2.1.9:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.3.2.tgz#a79a923666372b580f8e27f51845c6f7e8fbfbaf"
@@ -3858,7 +4124,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
 
-js-yaml@^3.5.1, js-yaml@^3.7.0:
+js-yaml@^3.4.3, js-yaml@^3.5.1:
   version "3.10.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
   dependencies:
@@ -3975,6 +4241,12 @@ k-rpc@^4.1.0:
     k-rpc-socket "^1.7.0"
     safe-buffer "^5.1.1"
 
+karma-source-map-support@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/karma-source-map-support/-/karma-source-map-support-1.2.0.tgz#1bf81e7bb4b089627ab352ec4179e117c406a540"
+  dependencies:
+    source-map-support "^0.4.1"
+
 killable@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
@@ -4025,6 +4297,27 @@ lcid@^1.0.0:
   dependencies:
     invert-kv "^1.0.0"
 
+less-loader@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-4.0.5.tgz#ae155a7406cac6acd293d785587fcff0f478c4dd"
+  dependencies:
+    clone "^2.1.1"
+    loader-utils "^1.1.0"
+    pify "^2.3.0"
+
+less@^2.7.2:
+  version "2.7.3"
+  resolved "https://registry.yarnpkg.com/less/-/less-2.7.3.tgz#cc1260f51c900a9ec0d91fb6998139e02507b63b"
+  optionalDependencies:
+    errno "^0.1.1"
+    graceful-fs "^4.1.2"
+    image-size "~0.5.0"
+    mime "^1.2.11"
+    mkdirp "^0.5.0"
+    promise "^7.1.1"
+    request "2.81.0"
+    source-map "^0.5.3"
+
 levn@^0.3.0, levn@~0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -4032,6 +4325,12 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+license-webpack-plugin@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.1.1.tgz#76b2cedccc78f139fd7877e576f756cfc141b8c2"
+  dependencies:
+    ejs "^2.5.7"
+
 linkify-it@^2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
@@ -4172,7 +4471,7 @@ lodash.clonedeep@^3.0.0:
     lodash._baseclone "^3.0.0"
     lodash._bindcallback "^3.0.0"
 
-lodash.clonedeep@^4.3.2:
+lodash.clonedeep@^4.3.2, lodash.clonedeep@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
 
@@ -4273,7 +4572,7 @@ lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-lodash@^4, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.4:
+lodash@^4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.4:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
@@ -4399,6 +4698,14 @@ md5.js@^1.3.4:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+md5@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
+  dependencies:
+    charenc "~0.0.1"
+    crypt "~0.0.1"
+    is-buffer "~1.1.1"
+
 mdurl@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
@@ -4425,7 +4732,7 @@ memory-chunk-store@^1.2.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/memory-chunk-store/-/memory-chunk-store-1.3.0.tgz#ae99e7e3b58b52db43d49d94722930d39459d0c4"
 
-memory-fs@^0.4.0, memory-fs@~0.4.1:
+memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
   dependencies:
@@ -4516,6 +4823,10 @@ mime@1.4.1, mime@^1.3.4, mime@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
 
+mime@^1.2.11, mime@^1.5.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+
 mimic-fn@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
@@ -4538,7 +4849,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
 
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
   dependencies:
@@ -4720,10 +5031,6 @@ ngx-clipboard@^9.0.0:
   dependencies:
     ngx-window-token "0.0.4"
 
-ngx-window-token@0.0.4:
-  version "0.0.4"
-  resolved "https://registry.yarnpkg.com/ngx-window-token/-/ngx-window-token-0.0.4.tgz#47e7aaa465411c4ab5f7ba17601bc593c956c736"
-
 ngx-infinite-scroll@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-0.7.0.tgz#a390c61c6a05ac14485e1c5bc8b4e6f6bd62fd6a"
@@ -4732,6 +5039,10 @@ ngx-pipes@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54"
 
+ngx-window-token@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/ngx-window-token/-/ngx-window-token-0.0.4.tgz#47e7aaa465411c4ab5f7ba17601bc593c956c736"
+
 no-case@^2.2.0:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
@@ -4806,6 +5117,10 @@ node-libs-browser@^2.0.0:
     util "^0.10.3"
     vm-browserify "0.0.4"
 
+node-modules-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/node-modules-path/-/node-modules-path-1.0.1.tgz#40096b08ce7ad0ea14680863af449c7c75a5d1c8"
+
 node-notifier@^4.1.0:
   version "4.6.1"
   resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-4.6.1.tgz#056d14244f3dcc1ceadfe68af9cff0c5473a33f3"
@@ -4857,6 +5172,30 @@ node-sass@^4.1.1:
     sass-graph "^2.2.4"
     stdout-stream "^1.4.0"
 
+node-sass@^4.3.0:
+  version "4.7.2"
+  resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e"
+  dependencies:
+    async-foreach "^0.1.3"
+    chalk "^1.1.1"
+    cross-spawn "^3.0.0"
+    gaze "^1.0.0"
+    get-stdin "^4.0.1"
+    glob "^7.0.3"
+    in-publish "^2.0.0"
+    lodash.assign "^4.2.0"
+    lodash.clonedeep "^4.3.2"
+    lodash.mergewith "^4.6.0"
+    meow "^3.7.0"
+    mkdirp "^0.5.1"
+    nan "^2.3.2"
+    node-gyp "^3.3.1"
+    npmlog "^4.0.0"
+    request "~2.79.0"
+    sass-graph "^2.2.4"
+    stdout-stream "^1.4.0"
+    "true-case-path" "^1.0.2"
+
 noop-logger@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
@@ -4906,6 +5245,10 @@ normalize.css@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-7.0.0.tgz#abfb1dd82470674e0322b53ceb1aaf412938e4bf"
 
+npm-font-source-sans-pro@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/npm-font-source-sans-pro/-/npm-font-source-sans-pro-1.0.2.tgz#c55c8ae368eebdbcaca65425a0d7e1f9a192a03e"
+
 npm-run-path@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -5014,7 +5357,7 @@ opener@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
 
-opn@^5.1.0:
+opn@^5.1.0, opn@~5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519"
   dependencies:
@@ -5263,7 +5606,7 @@ piece-length@^1.0.0:
   dependencies:
     closest-to "~2.0.0"
 
-pify@^2.0.0:
+pify@^2.0.0, pify@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
 
@@ -5318,7 +5661,7 @@ pluralize@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
 
-portfinder@^1.0.9:
+portfinder@^1.0.9, portfinder@~1.0.12:
   version "1.0.13"
   resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
   dependencies:
@@ -5353,6 +5696,13 @@ postcss-convert-values@^2.3.4:
     postcss "^5.0.11"
     postcss-value-parser "^3.1.2"
 
+postcss-custom-properties@^6.1.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-6.2.0.tgz#5d929a7f06e9b84e0f11334194c0ba9a30acfbe9"
+  dependencies:
+    balanced-match "^1.0.0"
+    postcss "^6.0.13"
+
 postcss-discard-comments@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
@@ -5391,6 +5741,38 @@ postcss-filter-plugins@^2.0.0:
     postcss "^5.0.4"
     uniqid "^4.0.0"
 
+postcss-load-config@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a"
+  dependencies:
+    cosmiconfig "^2.1.0"
+    object-assign "^4.1.0"
+    postcss-load-options "^1.2.0"
+    postcss-load-plugins "^2.3.0"
+
+postcss-load-options@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c"
+  dependencies:
+    cosmiconfig "^2.1.0"
+    object-assign "^4.1.0"
+
+postcss-load-plugins@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92"
+  dependencies:
+    cosmiconfig "^2.1.1"
+    object-assign "^4.1.0"
+
+postcss-loader@^2.0.8:
+  version "2.0.9"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.9.tgz#001fdf7bfeeb159405ee61d1bb8e59b528dbd309"
+  dependencies:
+    loader-utils "^1.1.0"
+    postcss "^6.0.0"
+    postcss-load-config "^1.2.0"
+    schema-utils "^0.3.0"
+
 postcss-merge-idents@^2.1.5:
   version "2.1.7"
   resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
@@ -5547,6 +5929,16 @@ postcss-unique-selectors@^2.0.2:
     postcss "^5.0.4"
     uniqs "^2.0.0"
 
+postcss-url@^7.1.2:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-7.3.0.tgz#cf2f45e06743cf43cfea25309f81cbc003dc783f"
+  dependencies:
+    mime "^1.4.1"
+    minimatch "^3.0.4"
+    mkdirp "^0.5.0"
+    postcss "^6.0.1"
+    xxhashjs "^0.2.1"
+
 postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
@@ -5568,7 +5960,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0
     source-map "^0.5.6"
     supports-color "^3.2.3"
 
-postcss@^6.0.1:
+postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.13:
   version "6.0.14"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885"
   dependencies:
@@ -5642,6 +6034,12 @@ promise-inflight@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
 
+promise@^7.1.1:
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+  dependencies:
+    asap "~2.0.3"
+
 proxy-addr@~2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
@@ -5653,6 +6051,10 @@ prr@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
 
+prr@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+
 pseudomap@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -5715,6 +6117,10 @@ qs@6.5.1, qs@~6.5.1:
   version "6.5.1"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
 
+qs@~6.3.0:
+  version "6.3.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c"
+
 qs@~6.4.0:
   version "6.4.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@@ -6077,10 +6483,39 @@ request@2.81.0:
     tunnel-agent "^0.6.0"
     uuid "^3.0.0"
 
+request@~2.79.0:
+  version "2.79.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.11.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~2.0.6"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    qs "~6.3.0"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "~0.4.1"
+    uuid "^3.0.0"
+
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
 
+require-from-string@^1.1.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
+
 require-main-filename@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
@@ -6215,6 +6650,12 @@ rxjs@^5.4.2:
   dependencies:
     symbol-observable "^1.0.1"
 
+rxjs@^5.5.2:
+  version "5.5.5"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.5.tgz#e164f11d38eaf29f56f08c3447f74ff02dd84e97"
+  dependencies:
+    symbol-observable "1.0.1"
+
 safe-buffer@5.1.1, safe-buffer@^5.0.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@@ -6257,6 +6698,10 @@ sass-resources-loader@^1.2.1:
     glob "^7.1.1"
     loader-utils "^1.0.4"
 
+sax@0.5.x:
+  version "0.5.8"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
+
 sax@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -6429,6 +6874,12 @@ signal-exit@^3.0.0:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
+silent-error@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/silent-error/-/silent-error-1.1.0.tgz#2209706f1c850a9f1d10d0d840918b46f26e1bc9"
+  dependencies:
+    debug "^2.2.0"
+
 simple-concat@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6"
@@ -6555,7 +7006,7 @@ source-list-map@~0.1.7:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
 
-source-map-loader@^0.2.1:
+source-map-loader@^0.2.0, source-map-loader@^0.2.1:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.3.tgz#d4b0c8cd47d54edce3e6bfa0f523f452b5b0e521"
   dependencies:
@@ -6582,7 +7033,7 @@ source-map-resolve@^0.5.0:
     source-map-url "^0.4.0"
     urix "^0.1.0"
 
-source-map-support@^0.4.0, source-map-support@^0.4.15, source-map-support@^0.4.2:
+source-map-support@^0.4.0, source-map-support@^0.4.1, source-map-support@^0.4.15, source-map-support@^0.4.2:
   version "0.4.18"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
   dependencies:
@@ -6596,17 +7047,17 @@ source-map-url@~0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9"
 
-source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3:
-  version "0.5.7"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
-
-source-map@^0.1.38:
+source-map@0.1.x, source-map@^0.1.38:
   version "0.1.43"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
   dependencies:
     amdefine ">=0.0.4"
 
-source-map@^0.4.2:
+source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+
+source-map@^0.4.2, source-map@~0.4.1:
   version "0.4.4"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
   dependencies:
@@ -6616,10 +7067,6 @@ source-map@^0.6.1, source-map@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 
-source-sans-pro@^2.0.10:
-  version "2.0.10"
-  resolved "https://registry.yarnpkg.com/source-sans-pro/-/source-sans-pro-2.0.10.tgz#c1ca859cf164a088944c5e83745085e87cd533a9"
-
 spdx-correct@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -6870,10 +7317,16 @@ strip-indent@^1.0.1:
   dependencies:
     get-stdin "^4.0.1"
 
-strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
+strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
 
+style-loader@^0.13.1:
+  version "0.13.2"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.2.tgz#74533384cf698c7104c7951150b49717adc2f3bb"
+  dependencies:
+    loader-utils "^1.0.2"
+
 style-loader@^0.19.0:
   version "0.19.0"
   resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.0.tgz#7258e788f0fee6a42d710eaf7d6c2412a4c50759"
@@ -6881,6 +7334,25 @@ style-loader@^0.19.0:
     loader-utils "^1.0.2"
     schema-utils "^0.3.0"
 
+stylus-loader@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.1.tgz#77f4b34fd030d25b2617bcf5513db5b0730c4089"
+  dependencies:
+    loader-utils "^1.0.2"
+    lodash.clonedeep "^4.5.0"
+    when "~3.6.x"
+
+stylus@^0.54.5:
+  version "0.54.5"
+  resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79"
+  dependencies:
+    css-parse "1.7.x"
+    debug "*"
+    glob "7.0.x"
+    mkdirp "0.5.x"
+    sax "0.5.x"
+    source-map "0.1.x"
+
 supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -6909,6 +7381,10 @@ svgo@^0.7.0:
     sax "~1.2.1"
     whet.extend "~0.9.9"
 
+symbol-observable@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
+
 symbol-observable@^1.0.1:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
@@ -7058,6 +7534,10 @@ tough-cookie@~2.3.0, tough-cookie@~2.3.3:
   dependencies:
     punycode "^1.4.1"
 
+tree-kill@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
+
 trim-newlines@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -7070,6 +7550,12 @@ trim@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
 
+"true-case-path@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62"
+  dependencies:
+    glob "^6.0.4"
+
 tryit@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
@@ -7177,6 +7663,10 @@ tunnel-agent@^0.6.0:
   dependencies:
     safe-buffer "^5.0.1"
 
+tunnel-agent@~0.4.1:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
+
 tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -7208,6 +7698,10 @@ typescript@^2.5.2:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.1.tgz#ef39cdea27abac0b500242d6726ab90e0c846631"
 
+typescript@~2.6.1:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
+
 uc.micro@^1.0.1, uc.micro@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192"
@@ -7219,6 +7713,13 @@ uglify-es@^3.1.3:
     commander "~2.11.0"
     source-map "~0.6.1"
 
+uglify-es@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.2.2.tgz#15c62b7775002c81b7987a1c49ecd3f126cace73"
+  dependencies:
+    commander "~2.12.1"
+    source-map "~0.6.1"
+
 uglify-js@3.1.x, uglify-js@^3.0.6:
   version "3.1.7"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.7.tgz#13379168b7fcf132ed977254a7802e0a294b1ffb"
@@ -7259,6 +7760,18 @@ uglifyjs-webpack-plugin@^1.0.1:
     webpack-sources "^1.0.1"
     worker-farm "^1.4.1"
 
+uglifyjs-webpack-plugin@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.2.tgz#8a9abc238d01a33daaf86fa9a84c7ebc1e67b0f9"
+  dependencies:
+    cacache "^10.0.0"
+    find-cache-dir "^1.0.0"
+    schema-utils "^0.3.0"
+    source-map "^0.6.1"
+    uglify-es "^3.2.0"
+    webpack-sources "^1.0.1"
+    worker-farm "^1.4.1"
+
 uid-number@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
@@ -7566,6 +8079,20 @@ webpack-bundle-analyzer@^2.8.2:
     opener "^1.4.3"
     ws "^2.3.1"
 
+webpack-concat-plugin@^1.4.2:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/webpack-concat-plugin/-/webpack-concat-plugin-1.4.2.tgz#b60bbb626ce5001911809d6e2329fa32f4978a88"
+  dependencies:
+    md5 "^2.2.1"
+    uglify-js "^2.8.29"
+
+webpack-core@^0.6.8:
+  version "0.6.9"
+  resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2"
+  dependencies:
+    source-list-map "~0.1.7"
+    source-map "~0.4.1"
+
 webpack-dev-middleware@^1.11.0:
   version "1.12.0"
   resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz#d34efefb2edda7e1d3b5dbe07289513219651709"
@@ -7576,6 +8103,16 @@ webpack-dev-middleware@^1.11.0:
     range-parser "^1.0.3"
     time-stamp "^2.0.0"
 
+webpack-dev-middleware@~1.12.0:
+  version "1.12.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
+  dependencies:
+    memory-fs "~0.4.1"
+    mime "^1.5.0"
+    path-is-absolute "^1.0.0"
+    range-parser "^1.0.3"
+    time-stamp "^2.0.0"
+
 webpack-dev-server@^2.4.5:
   version "2.9.4"
   resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.4.tgz#7883e61759c6a4b33e9b19ec4037bd4ab61428d1"
@@ -7608,6 +8145,38 @@ webpack-dev-server@^2.4.5:
     webpack-dev-middleware "^1.11.0"
     yargs "^6.6.0"
 
+webpack-dev-server@~2.9.3:
+  version "2.9.7"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.7.tgz#100ad6a14775478924d417ca6dcfb9d52a98faed"
+  dependencies:
+    ansi-html "0.0.7"
+    array-includes "^3.0.3"
+    bonjour "^3.5.0"
+    chokidar "^1.6.0"
+    compression "^1.5.2"
+    connect-history-api-fallback "^1.3.0"
+    debug "^3.1.0"
+    del "^3.0.0"
+    express "^4.16.2"
+    html-entities "^1.2.0"
+    http-proxy-middleware "~0.17.4"
+    import-local "^0.1.1"
+    internal-ip "1.2.0"
+    ip "^1.1.5"
+    killable "^1.0.0"
+    loglevel "^1.4.1"
+    opn "^5.1.0"
+    portfinder "^1.0.9"
+    selfsigned "^1.9.1"
+    serve-index "^1.7.2"
+    sockjs "0.3.18"
+    sockjs-client "1.1.4"
+    spdy "^3.4.1"
+    strip-ansi "^3.0.1"
+    supports-color "^4.2.1"
+    webpack-dev-middleware "^1.11.0"
+    yargs "^6.6.0"
+
 webpack-dll-bundles-plugin@^1.0.0-beta.5:
   version "1.0.0-beta.5"
   resolved "https://registry.yarnpkg.com/webpack-dll-bundles-plugin/-/webpack-dll-bundles-plugin-1.0.0-beta.5.tgz#cfb109710a88c3eeb557fcc38be0c5015a54196d"
@@ -7615,7 +8184,7 @@ webpack-dll-bundles-plugin@^1.0.0-beta.5:
     find-root "^1.0.0"
     jsonfile "^2.4.0"
 
-webpack-merge@~4.1.0:
+webpack-merge@^4.1.0, webpack-merge@~4.1.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.1.tgz#f1197a0a973e69c6fbeeb6d658219aa8c0c13555"
   dependencies:
@@ -7636,6 +8205,13 @@ webpack-sources@^0.1.2, webpack-sources@^0.1.4:
     source-list-map "~0.1.7"
     source-map "~0.5.3"
 
+webpack-sources@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"
+  dependencies:
+    source-list-map "^2.0.0"
+    source-map "~0.6.1"
+
 webpack-sources@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.2.tgz#d0148ec083b3b5ccef1035a6b3ec16442983b27a"
@@ -7643,6 +8219,12 @@ webpack-sources@^1.0.1:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
+webpack-subresource-integrity@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.0.3.tgz#c0606d40090b070cde428bec8df3603216e472eb"
+  dependencies:
+    webpack-core "^0.6.8"
+
 webpack@^3.3.0:
   version "3.8.1"
   resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.8.1.tgz#b16968a81100abe61608b0153c9159ef8bb2bd83"
@@ -7670,6 +8252,33 @@ webpack@^3.3.0:
     webpack-sources "^1.0.1"
     yargs "^8.0.2"
 
+webpack@~3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725"
+  dependencies:
+    acorn "^5.0.0"
+    acorn-dynamic-import "^2.0.0"
+    ajv "^5.1.5"
+    ajv-keywords "^2.0.0"
+    async "^2.1.2"
+    enhanced-resolve "^3.4.0"
+    escope "^3.6.0"
+    interpret "^1.0.0"
+    json-loader "^0.5.4"
+    json5 "^0.5.1"
+    loader-runner "^2.3.0"
+    loader-utils "^1.1.0"
+    memory-fs "~0.4.1"
+    mkdirp "~0.5.0"
+    node-libs-browser "^2.0.0"
+    source-map "^0.5.3"
+    supports-color "^4.2.1"
+    tapable "^0.2.7"
+    uglifyjs-webpack-plugin "^0.4.6"
+    watchpack "^1.4.0"
+    webpack-sources "^1.0.1"
+    yargs "^8.0.2"
+
 websocket-driver@>=0.5.1:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"
@@ -7728,6 +8337,10 @@ webtorrent@^0.98.0:
     xtend "^4.0.1"
     zero-fill "^2.2.3"
 
+when@~3.6.x:
+  version "3.6.4"
+  resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
+
 whet.extend@~0.9.9:
   version "0.9.9"
   resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
@@ -7833,6 +8446,12 @@ xtend@4.0.1, xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
 
+xxhashjs@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.1.tgz#9bbe9be896142976dfa34c061b2d068c43d30de0"
+  dependencies:
+    cuint latest
+
 y18n@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
@@ -7956,6 +8575,6 @@ zero-fill@^2.2.3:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/zero-fill/-/zero-fill-2.2.3.tgz#a3def06ba5e39ae644850bb4ca2ad4112b4855e9"
 
-zone.js@~0.8.5:
+zone.js@^0.8.14, zone.js@~0.8.5:
   version "0.8.18"
   resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.18.tgz#8cecb3977fcd1b3090562ff4570e2847e752b48d"
-- 
2.41.0