From b50d8418621a38d1c5da46d1586c5a40f5cbcc63 Mon Sep 17 00:00:00 2001 From: Alexis Lahouze Date: Tue, 7 Nov 2017 22:41:52 +0100 Subject: [PATCH] Improve webpack configuration. --- .gitignore | 3 +- config/helpers.js | 10 +++ config/webpack.common.js | 125 ++++++++++++++++++++++++++ config/webpack.dev.js | 32 +++++++ config/webpack.prod.js | 39 ++++++++ package.json | 10 ++- src/main.ts | 7 +- src/polyfills.ts | 11 +++ tsconfig.json => src/tsconfig.json | 0 src/vendor.ts | 13 +++ webpack.config.js | 137 +---------------------------- 11 files changed, 246 insertions(+), 141 deletions(-) create mode 100644 config/helpers.js create mode 100644 config/webpack.common.js create mode 100644 config/webpack.dev.js create mode 100644 config/webpack.prod.js create mode 100644 src/polyfills.ts rename tsconfig.json => src/tsconfig.json (100%) create mode 100644 src/vendor.ts diff --git a/.gitignore b/.gitignore index 7c262db..a4409f0 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,5 @@ tags # End of https://www.gitignore.io/api/vim,node -/build +/dist + diff --git a/config/helpers.js b/config/helpers.js new file mode 100644 index 0000000..a11fa77 --- /dev/null +++ b/config/helpers.js @@ -0,0 +1,10 @@ +var path = require('path'); + +var _root = path.resolve(__dirname, '..'); + +function root(args) { + args = Array.prototype.slice.call(arguments, 0); + return path.join.apply(path, [_root].concat(args)); +} + +exports.root = root; diff --git a/config/webpack.common.js b/config/webpack.common.js new file mode 100644 index 0000000..f7ddaf9 --- /dev/null +++ b/config/webpack.common.js @@ -0,0 +1,125 @@ +var webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var helpers = require('./helpers'); + +module.exports = { + entry: { + 'polyfills': './src/polyfills.ts', + 'vendor': './src/vendor.ts', + 'app': './src/main.ts', + 'styles': './src/main.scss' + }, + + resolve: { + extensions: ['.ts', '.js'], + }, + + module: { + rules: [{ + enforce: 'pre', + test: /webpack\.config\.js$/, + include: helpers.root('src', 'app'), + loader: 'eslint-loader', + options: { + useEslintrc: false, + emitWarning: true, + emitError: true, + failOnWarning: true, + failOnError: true, + baseConfig: 'webpack', + rules: { + indent: ['error', 4] + }, + }, + }, { + // Javascript + enforce: 'pre', + test: /\.jsx?$/, + include: helpers.root('src', 'app'), + loader: 'eslint-loader', + options: { + useEslintrc: false, + emitWarning: false, + emitError: true, + failOnWarning: false, + failOnError: true, + baseConfig: 'angular', + rules: { + indent: ['error', 4] + }, + plugins: [ + 'angular', + 'html', + 'security', + 'this', + 'jquery', + 'promise' + ] + }, + // }, { + // test: /\.jsx?$/, + // exclude: /node_modules/, + // loader: 'babel-loader' + }, { + test: /\.ts?$/, + //exclude: /node_modules/, + use: [ + { + loader: 'awesome-typescript-loader', + options: { configFileName: helpers.root('src', 'tsconfig.json') } + }, + 'angular2-template-loader' + ] + }, { + test: /\.html$/, + loader: 'html-loader' + }, { + test: /\.less$/, + use: [ + 'style-loader', + 'css-loader', + 'less-loader', + ] + }, { + test: /\.css$/, + exclude: helpers.root('src', 'app'), + loader: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader?sourceMap' + }) + }, { + test: /\.css$/, + include: helpers.root('src', 'app'), + loader: 'raw-loader' + }, { + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader', + ] + }, { + test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, + loader: 'file-loader?name=assets/[name].[hash].[ext]' + }] + }, + + plugins: [ + // Workaround for angular/angular#11580 + new webpack.ContextReplacementPlugin( + // The (\\|\/) piece accounts for path separators in *nix and Windows + /angular(\\|\/)core(\\|\/)@angular/, + helpers.root('./src'), // location of your src + {} // a map of your routes + ), + + new webpack.optimize.CommonsChunkPlugin({ + name: ['app', 'vendor', 'polyfills'] + }), + + new HtmlWebpackPlugin({ + template: 'src/index.ejs' + }) + ], +}; diff --git a/config/webpack.dev.js b/config/webpack.dev.js new file mode 100644 index 0000000..c723c66 --- /dev/null +++ b/config/webpack.dev.js @@ -0,0 +1,32 @@ +// vim: set tw=80 ts=2 sw=2 sts=2: + +var webpackMerge = require('webpack-merge'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var commonConfig = require('./webpack.common.js'); +var helpers = require('./helpers'); + +module.exports = webpackMerge(commonConfig, { + devtool: 'cheap-module-eval-source-map', + + output: { + path: helpers.root('dist'), + publicPath: '/', + filename: '[name].js', + chunkFilename: '[id].chunk.js' + }, + + plugins: [ + new ExtractTextPlugin('[name].css') + ], + + devServer: { + proxy: { + '/api': { + target: 'http://localhost:5000', + secure: false + } + }, + historyApiFallback: true, + stats: 'minimal' + } +}); diff --git a/config/webpack.prod.js b/config/webpack.prod.js new file mode 100644 index 0000000..d5a0395 --- /dev/null +++ b/config/webpack.prod.js @@ -0,0 +1,39 @@ +/* jshint esversion: 6 */ +var webpack = require('webpack'); +var webpackMerge = require('webpack-merge'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var commonConfig = require('./webpack.common.js'); +var helpers = require('./helpers'); + +const ENV = process.env.NODE_ENV = process.env.ENV = 'production'; + +module.exports = webpackMerge(commonConfig, { + devtool: 'source-map', + + output: { + path: helpers.root('dist'), + publicPath: '/', + filename: '[name].[hash].js', + chunkFilename: '[id].[hash].chunk.js' + }, + + plugins: [ + new webpack.NoEmitOnErrorsPlugin(), + new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618 + mangle: { + keep_fnames: true + } + }), + new ExtractTextPlugin('[name].[hash].css'), + new webpack.DefinePlugin({ + 'process.env': { + 'ENV': JSON.stringify(ENV) + } + }), + new webpack.LoaderOptionsPlugin({ + htmlLoader: { + minimize: false // workaround for ng2 + } + }) + ] +}); diff --git a/package.json b/package.json index 12349b8..cabe020 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "repository": "https://git.lahouze.org/xals/accountant", "license": "AGPL-1.0", "devDependencies": { + "angular2-template-loader": "^0.6.2", + "awesome-typescript-loader": "^3.3.0", "babel-core": "^6.26.0", "babel-eslint": "^8.0.1", "babel-loader": "^7.1.2", @@ -36,7 +38,8 @@ "typescript": "^2.4.2", "url-loader": "^0.6.2", "webpack": "^3.8.1", - "webpack-dev-server": "^2.9.3" + "webpack-dev-server": "^2.9.3", + "webpack-merge": "^4.1.1" }, "dependencies": { "@angular/animations": "^4.4.6", @@ -65,7 +68,8 @@ "zone.js": "^0.8.17" }, "scripts": { - "build": "webpack --config webpack.config.js", - "dev": "webpack-dev-server --debug --devtool eval --config webpack.config.js --progress --colors --hot --content-base build" + "build": "rm -Rf dist && webpack --config config/webpack.prod.js --progress --profile --bail", + "test": "karma start", + "dev": "webpack-dev-server --config config/webpack.dev.js --inline --progress --colors" } } diff --git a/src/main.ts b/src/main.ts index 07b1480..0a9a842 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,12 @@ // vim: set tw=80 ts=2 sw=2 sts=2 : +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { enableProdMode } from '@angular/core'; + import { AppModule } from './app.module'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +if (process.env.ENV === 'production') { + enableProdMode(); +} platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..b47fbf5 --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,11 @@ +import 'core-js/es6'; +import 'core-js/es7/reflect'; +require('zone.js/dist/zone'); + +if (process.env.ENV === 'production') { + // Production +} else { + // Development and test + Error['stackTraceLimit'] = Infinity; + require('zone.js/dist/long-stack-trace-zone'); +} diff --git a/tsconfig.json b/src/tsconfig.json similarity index 100% rename from tsconfig.json rename to src/tsconfig.json diff --git a/src/vendor.ts b/src/vendor.ts new file mode 100644 index 0000000..caef0d0 --- /dev/null +++ b/src/vendor.ts @@ -0,0 +1,13 @@ +// Angular +import '@angular/platform-browser'; +import '@angular/platform-browser-dynamic'; +import '@angular/core'; +import '@angular/common'; +import '@angular/http'; +import '@angular/router'; + +// RxJS +import 'rxjs'; + +// Other vendors for example jQuery, Lodash or Bootstrap +// You can import js, ts, css, sass, ... diff --git a/webpack.config.js b/webpack.config.js index 3942c0d..26df33c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,136 +1 @@ -/* jshint esversion: 6 */ -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const webpack = require('webpack'); - -module.exports = { - context: path.resolve(__dirname, 'src'), - entry: { - "main": [ - './main.ts' - ], - "styles": [ - './main.scss' - ] - }, - devtool: 'source-map', - resolve: { - extensions: ['.js', '.jsx', '.ts', '.tsx', '.html'], - }, - module: { - rules: [{ - enforce: 'pre', - test: /webpack\.config\.js$/, - include: path.resolve(__dirname), - loader: 'eslint-loader', - options: { - useEslintrc: false, - emitWarning: true, - emitError: true, - failOnWarning: true, - failOnError: true, - baseConfig: 'webpack', - rules: { - indent: ['error', 4] - }, - }, - }, { - // Javascript - enforce: 'pre', - test: /\.jsx?$/, - //include: path.resolve(__dirname, 'src'), - loader: 'eslint-loader', - options: { - useEslintrc: false, - emitWarning: false, - emitError: true, - failOnWarning: false, - failOnError: true, - baseConfig: 'angular', - rules: { - indent: ['error', 4] - }, - plugins: [ - 'angular', - 'html', - 'security', - 'this', - 'jquery', - 'promise' - ] - }, - }, { - test: /\.jsx?$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, { - test: /\.tsx?$/, - exclude: /node_modules/, - loader: 'ts-loader' - }, { - test: /\.html$/, - use: [ - 'ngtemplate-loader?relativeTo=/accountant-ui/src', - 'html-loader' - ] - }, { - test: /\.less$/, - use: [ - 'style-loader', - 'css-loader', - 'less-loader', - ] - }, { - test: /\.css$/, - use: [ - 'style-loader', - 'css-loader', - ] - }, { - test: /\.scss$/, - use: [ - 'style-loader', - 'css-loader', - 'sass-loader', - ] - }, { - test: /\.(png|woff|woff2|eot|ttf|svg)$/, - loader: 'url-loader?limit=100000' - }] - }, - plugins: [ - new webpack.ProvidePlugin({ - "window.jQuery": "jquery" - }), - new webpack.ContextReplacementPlugin( - /angular(\\|\/)core(\\|\/)@angular/, - path.resolve(__dirname, './') - ), - new HtmlWebpackPlugin({ - title: 'Accountant', - template: 'index.ejs', - hash: false, - inject: true, - compile: true, - minify: false, - chunks: 'all' - }) - ], - output: { - path: path.resolve(__dirname, 'build'), - filename: '[name].bundle.js', - chunkFilename: '[name].chunk.js' - //publicPath: 'js' - }, - devServer: { - proxy: { - '/api': { - target: 'http://localhost:5000', - secure: false - } - }, - hot: true, - noInfo: false, - quiet: false, - } -}; +module.exports = require('./config/webpack.dev.js');