diff --git a/client/.gitignore b/client/.gitignore index 27e212f08..fa5433194 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -1,2 +1,4 @@ /dist/ /node_modules +/compiled +/stats.json diff --git a/client/config/empty.js b/client/config/empty.js new file mode 100644 index 000000000..33acae188 --- /dev/null +++ b/client/config/empty.js @@ -0,0 +1,8 @@ +module.exports = { + NgProbeToken: {}, + HmrState: function () {}, + _createConditionalRootRenderer: function (rootRenderer, extraTokens, coreTokens) { + return rootRenderer + }, + __platform_browser_private__: {} +} diff --git a/client/config/helpers.js b/client/config/helpers.js index 6268d2628..ca5923472 100644 --- a/client/config/helpers.js +++ b/client/config/helpers.js @@ -1,13 +1,17 @@ const path = require('path') +// Helper functions const ROOT = path.resolve(__dirname, '..') - -console.log('root directory:', root() + '\n') +const EVENT = process.env.npm_lifecycle_event || '' function hasProcessFlag (flag) { return process.argv.join('').indexOf(flag) > -1 } +function hasNpmFlag (flag) { + return EVENT.includes(flag) +} + function isWebpackDevServer () { return process.argv[1] && !!(/webpack-dev-server$/.exec(process.argv[1])) } @@ -18,5 +22,6 @@ function root (args) { } exports.hasProcessFlag = hasProcessFlag +exports.hasNpmFlag = hasNpmFlag exports.isWebpackDevServer = isWebpackDevServer exports.root = root diff --git a/client/config/resource-override.js b/client/config/resource-override.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/config/webpack.common.js b/client/config/webpack.common.js index 7631af6b9..09d6f72b5 100644 --- a/client/config/webpack.common.js +++ b/client/config/webpack.common.js @@ -1,17 +1,20 @@ -const webpack = require('webpack') const helpers = require('./helpers') /* * Webpack Plugins */ -const CopyWebpackPlugin = require('copy-webpack-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin const AssetsPlugin = require('assets-webpack-plugin') +const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin') const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin') +const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin +const HtmlWebpackPlugin = require('html-webpack-plugin') const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin') +const ngcWebpack = require('ngc-webpack') + const WebpackNotifierPlugin = require('webpack-notifier') /* @@ -29,7 +32,8 @@ const METADATA = { * See: http://webpack.github.io/docs/configuration.html#cli */ module.exports = function (options) { - var isProd = options.env === 'production' + const isProd = options.env === 'production' + const AOT = isProd return { @@ -49,9 +53,10 @@ module.exports = function (options) { * See: http://webpack.github.io/docs/configuration.html#entry */ entry: { - 'polyfills': './src/polyfills.ts', - 'vendor': './src/vendor.ts', - 'main': './src/main.ts' + 'polyfills': './src/polyfills.browser.ts', + 'main': AOT + ? './src/main.browser.aot.ts' + : './src/main.browser.ts' }, /* @@ -67,7 +72,7 @@ module.exports = function (options) { */ extensions: [ '.ts', '.js', '.json', '.scss' ], - modules: [helpers.root('src'), 'node_modules'], + modules: [ helpers.root('src'), helpers.root('node_modules') ], alias: { 'video.js': 'video.js/dist/alt/video.novtt' @@ -90,10 +95,18 @@ module.exports = function (options) { */ { test: /\.ts$/, - loaders: [ + use: [ '@angularclass/hmr-loader?pretty=' + !isProd + '&prod=' + isProd, - 'awesome-typescript-loader', - 'angular2-template-loader' + 'awesome-typescript-loader?{configFileName: "tsconfig.webpack.json"}', + 'angular2-template-loader', + { + loader: 'ng-router-loader', + options: { + loader: 'async-system', + genDir: 'compiled', + aot: AOT + } + } ], exclude: [/\.(spec|e2e)\.ts$/] }, @@ -110,10 +123,11 @@ module.exports = function (options) { { test: /\.(sass|scss)$/, - loaders: ['css-to-string-loader', 'css-loader?sourceMap', 'resolve-url', 'sass-loader?sourceMap'] + use: ['css-to-string-loader', 'css-loader?sourceMap', 'resolve-url-loader', 'sass-loader?sourceMap'], + exclude: [ helpers.root('src', 'styles') ] }, - { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url?limit=10000&minetype=application/font-woff' }, - { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' }, + { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'url-loader?limit=10000&minetype=application/font-woff' }, + { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'file-loader' }, /* Raw loader support for *.html * Returns file content as string @@ -148,7 +162,7 @@ module.exports = function (options) { * * See: https://github.com/s-panferov/awesome-typescript-loader#forkchecker-boolean-defaultfalse */ - new ForkCheckerPlugin(), + new CheckerPlugin(), /* * Plugin: CommonsChunkPlugin @@ -158,8 +172,21 @@ module.exports = function (options) { * See: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin * See: https://github.com/webpack/docs/wiki/optimization#multi-page-app */ - new webpack.optimize.CommonsChunkPlugin({ - name: [ 'polyfills', 'vendor' ].reverse() + new CommonsChunkPlugin({ + name: 'polyfills', + chunks: ['polyfills'] + }), + + // This enables tree shaking of the vendor modules + new CommonsChunkPlugin({ + name: 'vendor', + chunks: ['main'], + minChunks: module => /node_modules\//.test(module.resource) + }), + + // Specify the correct order the scripts will be injected in + new CommonsChunkPlugin({ + name: ['polyfills', 'vendor'].reverse() }), /** @@ -171,8 +198,11 @@ module.exports = function (options) { */ new ContextReplacementPlugin( // The (\\|\/) piece accounts for path separators in *nix and Windows - /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, - helpers.root('src') // location of your src + /angular(\\|\/)core(\\|\/)src(\\|\/)linker/, + helpers.root('src'), // location of your src + { + // your Angular Async Route paths relative to this root directory + } ), /* @@ -255,6 +285,34 @@ module.exports = function (options) { precision: 10 } } + }), + + // Fix Angular 2 + new NormalModuleReplacementPlugin( + /facade(\\|\/)async/, + helpers.root('node_modules/@angular/core/src/facade/async.js') + ), + new NormalModuleReplacementPlugin( + /facade(\\|\/)collection/, + helpers.root('node_modules/@angular/core/src/facade/collection.js') + ), + new NormalModuleReplacementPlugin( + /facade(\\|\/)errors/, + helpers.root('node_modules/@angular/core/src/facade/errors.js') + ), + new NormalModuleReplacementPlugin( + /facade(\\|\/)lang/, + helpers.root('node_modules/@angular/core/src/facade/lang.js') + ), + new NormalModuleReplacementPlugin( + /facade(\\|\/)math/, + helpers.root('node_modules/@angular/core/src/facade/math.js') + ), + + new ngcWebpack.NgcWebpackPlugin({ + disabled: !AOT, + tsConfig: helpers.root('tsconfig.webpack.json'), + resourceOverride: helpers.root('config/resource-override.js') }) ], @@ -270,7 +328,9 @@ module.exports = function (options) { process: true, module: false, clearImmediate: false, - setImmediate: false + setImmediate: false, + setInterval: false, + setTimeout: false } } } diff --git a/client/config/webpack.dev.js b/client/config/webpack.dev.js index 964ea56a5..cea9d0306 100644 --- a/client/config/webpack.dev.js +++ b/client/config/webpack.dev.js @@ -1,6 +1,7 @@ const helpers = require('./helpers') const webpackMerge = require('webpack-merge') // used to merge webpack configs const commonConfig = require('./webpack.common.js') // the settings that are common to prod and dev +const path = require('path') /** * Webpack Plugins @@ -29,7 +30,7 @@ const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, { * See: http://webpack.github.io/docs/configuration.html#cli */ module.exports = function (env) { - return webpackMerge(commonConfig({env: ENV}), { + return webpackMerge(commonConfig({ env: ENV }), { /** * Developer tool to enhance debugging * diff --git a/client/config/webpack.prod.js b/client/config/webpack.prod.js index 447d47415..64d776f24 100644 --- a/client/config/webpack.prod.js +++ b/client/config/webpack.prod.js @@ -9,14 +9,15 @@ const commonConfig = require('./webpack.common.js') // the settings that are com /** * Webpack Plugins */ -// const ProvidePlugin = require('webpack/lib/ProvidePlugin') const DefinePlugin = require('webpack/lib/DefinePlugin') -const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const IgnorePlugin = require('webpack/lib/IgnorePlugin') const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') -// const IgnorePlugin = require('webpack/lib/IgnorePlugin') -// const DedupePlugin = require('webpack/lib/optimize/DedupePlugin') +const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin') +const ProvidePlugin = require('webpack/lib/ProvidePlugin') const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin') const WebpackMd5Hash = require('webpack-md5-hash') +const V8LazyParseWebpackPlugin = require('v8-lazy-parse-webpack-plugin') /** * Webpack Constants @@ -154,22 +155,67 @@ module.exports = function (env) { // comments: true, //debug beautify: false, // prod + output: { + comments: false + }, // prod mangle: { - screw_ie8: true, - keep_fnames: true + screw_ie8: true }, // prod compress: { screw_ie8: true, - warnings: false - }, // prod - comments: false // prod + warnings: false, + conditionals: true, + unused: true, + comparisons: true, + sequences: true, + dead_code: true, + evaluate: true, + if_return: true, + join_vars: true, + negate_iife: false // we need this for lazy v8 + } }), new NormalModuleReplacementPlugin( /angular2-hmr/, - helpers.root('config/modules/angular2-hmr-prod.js') + helpers.root('config/empty.js') ), + new NormalModuleReplacementPlugin( + /zone\.js(\\|\/)dist(\\|\/)long-stack-trace-zone/, + helpers.root('config/empty.js') + ), + + // AoT + // new NormalModuleReplacementPlugin( + // /@angular(\\|\/)upgrade/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /@angular(\\|\/)compiler/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /@angular(\\|\/)platform-browser-dynamic/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /dom(\\|\/)debug(\\|\/)ng_probe/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /dom(\\|\/)debug(\\|\/)by/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /src(\\|\/)debug(\\|\/)debug_node/, + // helpers.root('config/empty.js') + // ), + // new NormalModuleReplacementPlugin( + // /src(\\|\/)debug(\\|\/)debug_renderer/, + // helpers.root('config/empty.js') + // ), + /** * Plugin: IgnorePlugin * Description: Don’t generate modules for requests matching the provided RegExp. @@ -228,7 +274,7 @@ module.exports = function (env) { [/\*/, /(?:)/], [/\[?\(?/, /(?:)/] ], - customAttrAssign: [/\)?]?=/] + customAttrAssign: [/\)?\]?=/] }, // FIXME: Remove diff --git a/client/package.json b/client/package.json index e9f41959d..5d09f4c54 100644 --- a/client/package.json +++ b/client/package.json @@ -20,6 +20,7 @@ "dependencies": { "@angular/common": "~2.4.1", "@angular/compiler": "~2.4.1", + "@angular/compiler-cli": "^2.4.3", "@angular/core": "~2.4.1", "@angular/forms": "~2.4.1", "@angular/http": "~2.4.1", @@ -33,12 +34,13 @@ "@types/source-map": "^0.1.26", "@types/uglify-js": "^2.0.27", "@types/videojs": "0.0.30", - "@types/webpack": "^1.12.29", + "@types/webpack": "^2.0.0", "angular-pipes": "^5.0.0", "angular2-template-loader": "^0.6.0", "assets-webpack-plugin": "^3.4.0", - "awesome-typescript-loader": "^2.2.1", - "bootstrap-loader": "^2.0.0-beta.11", + "awesome-typescript-loader": "~3.0.0-beta.17", + "bootstrap": "^3.3.6", + "bootstrap-loader": "2.0.0-beta.18", "bootstrap-sass": "^3.3.6", "copy-webpack-plugin": "^4.0.0", "core-js": "^2.4.1", @@ -52,9 +54,11 @@ "intl": "^1.2.4", "json-loader": "^0.5.4", "ng2-bootstrap": "1.1.16-10", - "ng2-file-upload": "^1.1.0", + "ng2-file-upload": "^1.1.4-2", "ng2-meta": "^2.0.0", - "node-sass": "^3.10.0", + "ng-router-loader": "^1.0.2", + "ngc-webpack": "^1.1.0", + "node-sass": "^4.1.1", "normalize.css": "^5.0.0", "raw-loader": "^0.5.1", "reflect-metadata": "0.1.8", @@ -68,13 +72,14 @@ "ts-helpers": "^1.1.1", "tslint": "3.15.1", "tslint-loader": "^2.1.4", - "typescript": "~2.0.9", + "typescript": "~2.1.0", "url-loader": "^0.5.7", + "v8-lazy-parse-webpack-plugin": "^0.3.0", "video.js": "^5.11.9", "videojs-dock": "^2.0.2", - "webpack": "2.1.0-beta.25", + "webpack": "2.2.0-rc.3", "webpack-md5-hash": "0.0.5", - "webpack-merge": "^0.15.0", + "webpack-merge": "~2.3.1", "webpack-notifier": "^1.3.0", "webtorrent": "^0.98.0", "zone.js": "~0.7.2" diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts index 0635c2533..046690347 100644 --- a/client/src/app/account/account.service.ts +++ b/client/src/app/account/account.service.ts @@ -1,4 +1,6 @@ import { Injectable } from '@angular/core'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; import { AuthService } from '../core'; import { AuthHttp, RestExtractor } from '../shared'; diff --git a/client/src/app/admin/friends/shared/friend.service.ts b/client/src/app/admin/friends/shared/friend.service.ts index 85ac04ba0..e97459385 100644 --- a/client/src/app/admin/friends/shared/friend.service.ts +++ b/client/src/app/admin/friends/shared/friend.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; import { Friend } from './friend.model'; import { AuthHttp, RestExtractor, ResultList } from '../../../shared'; diff --git a/client/src/app/admin/requests/request-stats/request-stats.component.ts b/client/src/app/admin/requests/request-stats/request-stats.component.ts index 9e2af219c..66075e4b5 100644 --- a/client/src/app/admin/requests/request-stats/request-stats.component.ts +++ b/client/src/app/admin/requests/request-stats/request-stats.component.ts @@ -1,3 +1,4 @@ +import { setInterval } from 'timers' import { Component, OnInit, OnDestroy } from '@angular/core'; import { RequestService, RequestStats } from '../shared'; diff --git a/client/src/app/admin/requests/shared/request.service.ts b/client/src/app/admin/requests/shared/request.service.ts index aeec37448..55b28bcfc 100644 --- a/client/src/app/admin/requests/shared/request.service.ts +++ b/client/src/app/admin/requests/shared/request.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; import { RequestStats } from './request-stats.model'; import { AuthHttp, RestExtractor } from '../../../shared'; diff --git a/client/src/app/admin/users/shared/user.service.ts b/client/src/app/admin/users/shared/user.service.ts index 13be553c0..d9005b213 100644 --- a/client/src/app/admin/users/shared/user.service.ts +++ b/client/src/app/admin/users/shared/user.service.ts @@ -1,4 +1,6 @@ import { Injectable } from '@angular/core'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; import { AuthHttp, RestExtractor, ResultList, User } from '../../../shared'; diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index ce4fc04ff..3f2f1ace0 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,7 +1,7 @@ import { Component, ViewContainerRef } from '@angular/core'; import { Router } from '@angular/router'; -import { MetaService } from 'ng2-meta'; +import { MetaService } from 'ng2-meta/src'; @Component({ selector: 'my-app', templateUrl: './app.component.html', diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index e9bb800f4..fb71787c4 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -2,7 +2,8 @@ import { ApplicationRef, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { removeNgStyles, createNewHosts } from '@angularclass/hmr'; -import { MetaModule, MetaConfig } from 'ng2-meta'; +import { MetaModule, MetaConfig } from 'ng2-meta/src'; +import 'bootstrap-loader'; import { ENV_PROVIDERS } from './environment'; import { AppRoutingModule } from './app-routing.module'; diff --git a/client/src/app/app.service.ts b/client/src/app/app.service.ts index 033c21900..9b582e472 100644 --- a/client/src/app/app.service.ts +++ b/client/src/app/app.service.ts @@ -1,35 +1,35 @@ - import { Injectable } from '@angular/core'; +export type InternalStateType = { + [key: string]: any +}; + @Injectable() export class AppState { - _state = { }; - constructor() { ; } + public _state: InternalStateType = { }; // already return a clone of the current state - get state() { + public get state() { return this._state = this._clone(this._state); } // never allow mutation - set state(value) { + public set state(value) { throw new Error('do not mutate the `.state` directly'); } - - get(prop?: any) { + public get(prop?: any) { // use our state getter for the clone const state = this.state; return state.hasOwnProperty(prop) ? state[prop] : state; } - set(prop: string, value: any) { + public set(prop: string, value: any) { // internally mutate our state return this._state[prop] = value; } - - _clone(object) { + 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 1f0e322a9..bc276ed99 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -3,6 +3,8 @@ import { Headers, Http, Response, URLSearchParams } from '@angular/http'; import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/mergeMap'; // Do not use the barrel (dependency loop) import { AuthStatus } from '../../shared/auth/auth-status.model'; diff --git a/client/src/app/environment.ts b/client/src/app/environment.ts index 8bba89c4e..929e09490 100644 --- a/client/src/app/environment.ts +++ b/client/src/app/environment.ts @@ -4,19 +4,24 @@ import { enableDebugTools, disableDebugTools } from '@angular/platform-browser'; import { enableProdMode, ApplicationRef } from '@angular/core'; // Environment Providers -let 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 = function identity(value) { return value; }; +let _decorateModuleRef = function identity(value: T): T { return value; }; if ('production' === ENV) { - // Production - disableDebugTools(); enableProdMode(); + // Production + _decorateModuleRef = (modRef: any) => { + disableDebugTools(); + + return modRef; + }; + PROVIDERS = [ ...PROVIDERS, // custom providers in production diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts index 602726570..c4114aa02 100644 --- a/client/src/app/shared/auth/auth-http.service.ts +++ b/client/src/app/shared/auth/auth-http.service.ts @@ -80,12 +80,14 @@ export class AuthHttp extends Http { } } +export function useFactory(backend: XHRBackend, defaultOptions: RequestOptions, authService: AuthService) { + return new AuthHttp(backend, defaultOptions, authService); +} + export const AUTH_HTTP_PROVIDERS = [ { provide: AuthHttp, - useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, authService: AuthService) => { - return new AuthHttp(backend, defaultOptions, authService); - }, + useFactory, deps: [ XHRBackend, RequestOptions, AuthService ] }, ]; diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index f173ef06b..9d79b2f5e 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts @@ -1,6 +1,8 @@ import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; import { Search } from '../../shared'; import { SortField } from './sort-field.type'; diff --git a/client/src/app/videos/video-watch/video-magnet.component.html b/client/src/app/videos/video-watch/video-magnet.component.html index 9108c7258..3fa82f1be 100644 --- a/client/src/app/videos/video-watch/video-magnet.component.html +++ b/client/src/app/videos/video-watch/video-magnet.component.html @@ -3,7 +3,7 @@