Webpack Upgrade (#665)

* Update TODO comments & Remove old TODO comments

* Fix undefined bityRate pair

* Fix any props in TODO

* Add HashRouter

* Update publicPath

* Revert "Update publicPath"

This reverts commit 1ab9068df4d570cf50bc4f2fcd97bd775e9aa768.

* Use HashRouter only if site is downloaded

* Update conditions for router

* Update asset paths & Change publicPath in production

* Remove hoist-non-react-statistics

* Revert "Remove hoist-non-react-statistics"

This reverts commit abc017a3f3ca1a00bebdd9201f0d18770581d8c5.

* Add hoist-non-react-statics as dev depencency

* Initial tests

* Lock hoist-non-react-statics version

* Add webpack-include-assets & favicon-webpack plugins

* Add env var BUILD_DOWNLOADABLE

* Remove dll from prod build

* Speed up rebuild times

* Change var to const

* lodash tree-shacking finagling

* Make app aware of its serving location

* Fix failing test

* Remove downloadable plugin

* Merge hash-router and get build working

* Add missing package.

* Make app aware of its serving location

* Revert "Make app aware of its serving location"

This reverts commit 8dae3b399e0392272cde25d45443391f6fb6594e.

* Revert "Remove downloadable plugin"

* Move AutoDLLPlugin to be in dev only

* Remove require HtmlWebpackIncludeAssetsPlugin

* Remove extra file added

* Bring config up to date with webpack 2 rules, add multi threading and proper cache busting

* Fix favicons package from freezing build process

* Make exclude rules more simple

* update freezer webpack config

* Move webpack multithreading to full source map dev builds only

* update freezer webpack config (#687)

* Add HtmlWebpackIncludeAssetsPlugin
This commit is contained in:
HenryNguyen5 2017-12-30 15:29:04 -05:00 committed by Daniel Ternyak
parent 170dc64284
commit 616928c085
18 changed files with 162 additions and 134 deletions

View File

@ -11,10 +11,11 @@ import Swap from 'containers/Tabs/Swap';
import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage'; import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage';
import BroadcastTx from 'containers/Tabs/BroadcastTx'; import BroadcastTx from 'containers/Tabs/BroadcastTx';
import ErrorScreen from 'components/ErrorScreen'; import ErrorScreen from 'components/ErrorScreen';
import { Store } from 'redux';
import { AppState } from 'reducers';
// TODO: fix this
interface Props { interface Props {
store: any; store: Store<AppState>;
} }
interface State { interface State {

View File

@ -1,4 +1,4 @@
import { indexOf } from 'lodash'; import indexOf from 'lodash/indexOf';
export const filter = (i: any, arr: any[]) => { export const filter = (i: any, arr: any[]) => {
return -1 !== indexOf(arr, i) ? true : false; return -1 !== indexOf(arr, i) ? true : false;

View File

@ -7,7 +7,8 @@ import translate from 'translations';
import { combineAndUpper } from 'utils/formatters'; import { combineAndUpper } from 'utils/formatters';
import { Dropdown } from 'components/ui'; import { Dropdown } from 'components/ui';
import Spinner from 'components/ui/Spinner'; import Spinner from 'components/ui/Spinner';
import { without, intersection } from 'lodash'; import intersection from 'lodash/intersection';
import without from 'lodash/without';
import './CurrencySwap.scss'; import './CurrencySwap.scss';
export interface StateProps { export interface StateProps {

View File

@ -7,7 +7,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
</head> </head>

View File

@ -2,7 +2,7 @@
import BN from 'bn.js'; import BN from 'bn.js';
import { toBuffer, addHexPrefix, bufferToHex } from 'ethereumjs-util'; import { toBuffer, addHexPrefix, bufferToHex } from 'ethereumjs-util';
import { trimStart } from 'lodash'; import trimStart from 'lodash/trimStart';
// When encoding QUANTITIES (integers, numbers): encode as hex, prefix with "0x", the most compact representation (slight exception: zero should be represented as "0x0"). // When encoding QUANTITIES (integers, numbers): encode as hex, prefix with "0x", the most compact representation (slight exception: zero should be represented as "0x0").
export function hexEncodeQuantity(value: BN | Buffer): string { export function hexEncodeQuantity(value: BN | Buffer): string {

View File

@ -2,7 +2,6 @@ import { Wei } from 'libs/units';
import * as eth from './ether'; import * as eth from './ether';
import { IFullWallet } from 'libs/wallet'; import { IFullWallet } from 'libs/wallet';
import { ITransaction } from '../typings'; import { ITransaction } from '../typings';
export { signTransaction };
export { export {
enoughBalanceViaTx, enoughBalanceViaTx,
validateTx, validateTx,
@ -12,7 +11,7 @@ export {
computeIndexingHash computeIndexingHash
} from './ether'; } from './ether';
export * from './token'; export * from './token';
const signTransaction = async ( export const signTransaction = async (
t: ITransaction, t: ITransaction,
w: IFullWallet, w: IFullWallet,
accountBalance: Wei, accountBalance: Wei,

View File

@ -6,7 +6,7 @@ import TrezorConnect from 'vendor/trezor-connect';
import { DeterministicWallet } from './deterministic'; import { DeterministicWallet } from './deterministic';
import { getTransactionFields } from 'libs/transaction'; import { getTransactionFields } from 'libs/transaction';
import { mapValues } from 'lodash'; import mapValues from 'lodash/mapValues';
import { IFullWallet } from '../IWallet'; import { IFullWallet } from '../IWallet';

View File

@ -1,24 +0,0 @@
require('ethereumjs-abi');
require('ethereumjs-util');
require('ethereumjs-wallet');
require('hdkey');
require('idna-uts46');
require('lodash');
require('react');
require('react-dom');
require('react-markdown');
require('react-redux');
require('react-router');
require('react-router-redux');
require('redux');
require('redux-logger');
require('redux-saga');
require('wallet-address-validator');
require('store2');
require('whatwg-fetch');
require('moment');
require('prop-types');
require('qrcode');
require('qrcode.react');
require('bn.js');
require('classnames');

View File

@ -19,6 +19,7 @@
"ethereumjs-util": "5.1.2", "ethereumjs-util": "5.1.2",
"ethereumjs-wallet": "0.6.0", "ethereumjs-wallet": "0.6.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"hard-source-webpack-plugin": "^0.5.13",
"hdkey": "0.7.1", "hdkey": "0.7.1",
"idna-uts46": "1.1.0", "idna-uts46": "1.1.0",
"jsonschema": "1.2.2", "jsonschema": "1.2.2",
@ -62,6 +63,7 @@
"@types/redux-promise-middleware": "0.0.9", "@types/redux-promise-middleware": "0.0.9",
"@types/uuid": "3.4.3", "@types/uuid": "3.4.3",
"@types/webpack-env": "1.13.3", "@types/webpack-env": "1.13.3",
"autodll-webpack-plugin": "^0.3.8",
"awesome-typescript-loader": "3.4.1", "awesome-typescript-loader": "3.4.1",
"babel-minify-webpack-plugin": "0.2.0", "babel-minify-webpack-plugin": "0.2.0",
"bs58": "4.0.1", "bs58": "4.0.1",
@ -80,7 +82,6 @@
"friendly-errors-webpack-plugin": "1.6.1", "friendly-errors-webpack-plugin": "1.6.1",
"glob": "7.1.2", "glob": "7.1.2",
"hoist-non-react-statics": "2.3.1", "hoist-non-react-statics": "2.3.1",
"html-webpack-include-assets-plugin": "1.0.2",
"html-webpack-plugin": "2.30.1", "html-webpack-plugin": "2.30.1",
"husky": "0.14.3", "husky": "0.14.3",
"image-webpack-loader": "3.4.2", "image-webpack-loader": "3.4.2",
@ -102,7 +103,9 @@
"rimraf": "2.6.2", "rimraf": "2.6.2",
"sass-loader": "6.0.6", "sass-loader": "6.0.6",
"style-loader": "0.19.1", "style-loader": "0.19.1",
"thread-loader": "^1.1.2",
"ts-jest": "22.0.0", "ts-jest": "22.0.0",
"ts-loader": "^3.2.0",
"tslint": "5.8.0", "tslint": "5.8.0",
"tslint-config-prettier": "1.6.0", "tslint-config-prettier": "1.6.0",
"tslint-react": "3.3.3", "tslint-react": "3.3.3",
@ -135,7 +138,6 @@
"predev:https": "check-node-version --package", "predev:https": "check-node-version --package",
"tslint": "tslint --project . --exclude common/vendor/**/*", "tslint": "tslint --project . --exclude common/vendor/**/*",
"tscheck": "tsc --noEmit", "tscheck": "tsc --noEmit",
"postinstall": "webpack --config=./webpack_config/webpack.dll.js",
"start": "npm run dev", "start": "npm run dev",
"precommit": "lint-staged", "precommit": "lint-staged",
"formatAll": "formatAll":

View File

@ -3,9 +3,9 @@
"outDir": "./dist/", "outDir": "./dist/",
"sourceMap": true, "sourceMap": true,
"strictNullChecks": true, "strictNullChecks": true,
"module": "esnext", "module": "es2015",
"jsx": "react", "jsx": "react",
"target": "es5", "target": "es2015",
"allowJs": true, "allowJs": true,
"baseUrl": "./common/", "baseUrl": "./common/",
"lib": [ "lib": [

View File

@ -8,27 +8,38 @@ module.exports = {
srcPath: path.join(__dirname, './../common'), srcPath: path.join(__dirname, './../common'),
// add these dependencies to a standalone vendor bundle // add these dependencies to a standalone vendor bundle
vendor: [ vendor: [
'bip39',
'bn.js',
'classnames',
'ethereum-blockies',
'ethereumjs-abi',
'ethereumjs-tx',
'ethereumjs-util',
'ethereumjs-wallet',
'hdkey',
'idna-uts46',
'jsonschema',
'lodash',
'moment',
'normalizr',
'qrcode',
'qrcode.react',
'query-string',
'react', 'react',
'react-dom', 'react-dom',
'react-router', 'react-markdown',
'redux', 'react-redux',
'react-router-dom',
'react-router-redux', 'react-router-redux',
'react-transition-group',
'redux',
'redux-logger',
'redux-promise-middleware',
'redux-saga', 'redux-saga',
'scryptsy',
'store2',
'uuid',
'wallet-address-validator',
'whatwg-fetch' 'whatwg-fetch'
], ]
// Settings for webpack-image-loader image compression
imageCompressionOptions: {
optipng: {
optimizationLevel: 4
},
gifsicle: {
interlaced: false
},
mozjpeg: {
quality: 80
},
svgo: {
plugins: [{ removeViewBox: true }, { removeEmptyAttrs: false }, { sortAttrs: true }]
}
}
}; };

View File

@ -39,8 +39,7 @@ const devMiddleWare = require('webpack-dev-middleware')(compiler, {
'Access-Control-Allow-Headers': '*' 'Access-Control-Allow-Headers': '*'
}, },
watchOptions: { watchOptions: {
aggregateTimeout: 300, aggregateTimeout: 100
poll: true
} }
}); });
app.use(devMiddleWare); app.use(devMiddleWare);

View File

@ -19,6 +19,7 @@ _.loadersOptions = () => {
return { return {
minimize: isProd, minimize: isProd,
debug: !isProd,
options: { options: {
// css-loader relies on context // css-loader relies on context
context: process.cwd() context: process.cwd()

View File

@ -4,11 +4,10 @@ const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
const config = require('./config'); const config = require('./config');
const _ = require('./utils'); const _ = require('./utils');
const { CheckerPlugin } = require('awesome-typescript-loader');
module.exports = { const webpackConfig = {
entry: { entry: {
client: './common/index.tsx' client: './common/index.tsx'
}, },
@ -17,9 +16,6 @@ module.exports = {
filename: '[name].js', filename: '[name].js',
publicPath: config.publicPath publicPath: config.publicPath
}, },
performance: {
hints: process.env.NODE_ENV === 'production' ? 'warning' : false
},
resolve: { resolve: {
extensions: ['.ts', '.tsx', '.js', '.css', '.json', '.scss', '.less'], extensions: ['.ts', '.tsx', '.js', '.css', '.json', '.scss', '.less'],
modules: [ modules: [
@ -30,23 +26,26 @@ module.exports = {
] ]
}, },
module: { module: {
loaders: [ rules: [
{ {
test: /\.(ts|tsx)$/, test: /\.(ts|tsx)$/,
loaders: [ include: path.resolve(__dirname, '../common'),
{ loader: 'cache-loader' }, use: [{ loader: 'ts-loader', options: { happyPackMode: true, logLevel: 'info' } }],
{ exclude: ['assets', 'sass', 'vendor', 'translations/lang']
loader: 'awesome-typescript-loader' .map(dir => path.resolve(__dirname, `../common/${dir}`))
} .concat([path.resolve(__dirname, '../node_modules')])
],
exclude: [/node_modules/]
}, },
{ {
include: [
path.resolve(__dirname, '../common/assets'),
path.resolve(__dirname, '../node_modules')
],
exclude: /node_modules(?!\/font-awesome)/,
test: /\.(gif|png|jpe?g|svg)$/i, test: /\.(gif|png|jpe?g|svg)$/i,
loaders: [ use: [
{ {
loader: 'file-loader', loader: 'file-loader',
query: { options: {
hash: 'sha512', hash: 'sha512',
digest: 'hex', digest: 'hex',
name: '[path][name].[ext]?[hash:6]' name: '[path][name].[ext]?[hash:6]'
@ -54,38 +53,52 @@ module.exports = {
}, },
{ {
loader: 'image-webpack-loader', loader: 'image-webpack-loader',
query: config.imageCompressionOptions options: {
bypassOnDebug: true,
optipng: {
optimizationLevel: 4
},
gifsicle: {
interlaced: false
},
mozjpeg: {
quality: 80
},
svgo: {
plugins: [{ removeViewBox: true }, { removeEmptyAttrs: false }, { sortAttrs: true }]
}
}
} }
] ]
}, },
{ {
include: [
path.resolve(__dirname, '../common/assets'),
path.resolve(__dirname, '../node_modules')
],
exclude: /node_modules(?!\/font-awesome)/,
test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/, test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/,
loader: 'file-loader?limit=100000' loader: 'file-loader'
} }
] ]
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({
'process.env.BUILD_DOWNLOADABLE': JSON.stringify(!!process.env.BUILD_DOWNLOADABLE)
}),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
title: config.title, title: config.title,
template: path.resolve(__dirname, '../common/index.html'), template: path.resolve(__dirname, '../common/index.html'),
inject: true,
filename: _.outputIndexPath filename: _.outputIndexPath
}), }),
new HtmlWebpackIncludeAssetsPlugin({ assets: ['dll.vendor.js'], append: false }),
new FaviconsWebpackPlugin({
logo: path.resolve(__dirname, '../static/favicon/android-chrome-384x384.png'),
background: '#163151'
}),
new webpack.LoaderOptionsPlugin(_.loadersOptions()),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ {
from: _.cwd('./static'), from: _.cwd('./static'),
// to the root of dist path // to the root of dist path
to: './' to: './'
} }
]) ]),
new webpack.LoaderOptionsPlugin(_.loadersOptions())
], ],
target: _.target target: _.target
}; };
module.exports = webpackConfig;

View File

@ -4,31 +4,78 @@ const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const base = require('./webpack.base'); const base = require('./webpack.base');
const FriendlyErrors = require('friendly-errors-webpack-plugin'); const FriendlyErrors = require('friendly-errors-webpack-plugin');
const AutoDllPlugin = require('autodll-webpack-plugin');
const config = require('./config');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const threadLoader = require('thread-loader');
base.devtool = process.env.SLOW_BUILD_SPEED const fullSourceMap = process.env.SLOW_BUILD_SPEED;
? 'source-map' if (fullSourceMap) {
: 'cheap-module-eval-source-map'; base.devtool = fullSourceMap ? 'source-map' : 'cheap-module-eval-source-map';
base.module.loaders.push( threadLoader.warmup(
{
// pool options, like passed to loader options
// must match loader options to boot the correct pool
happyPackMode: true,
logLevel: 'info'
},
[
// modules to load
// can be any module, i. e.
'ts-loader'
]
);
base.module.rules[0].use.unshift({
loader: 'thread-loader',
options: {
workers: 4
}
});
}
base.performance = { hints: false };
base.module.rules.push(
{ {
test: /\.css$/, test: /\.css$/,
loaders: ['style-loader', 'css-loader'] include: path.resolve(__dirname, '../common/vendor'),
use: ['style-loader', 'css-loader']
}, },
{ {
test: /\.scss$/, test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader'] include: ['components', 'containers', 'sass']
.map(dir => path.resolve(__dirname, `../common/${dir}`))
.concat([path.resolve(__dirname, '../node_modules')]),
exclude: /node_modules(?!\/font-awesome)/,
use: ['style-loader', 'css-loader', 'sass-loader']
}, },
{ {
test: /\.less$/, test: /\.less$/,
loaders: ['style-loader', 'css-loader', 'less-loader'] include: path.resolve(__dirname, '../common/assets/styles'),
use: ['style-loader', 'css-loader', 'less-loader']
} }
); );
base.plugins.push( base.plugins.push(
new webpack.DllReferencePlugin({ new AutoDllPlugin({
context: path.join(__dirname, '../common'), inject: true, // will inject the DLL bundles to index.html
manifest: require('../dll/vendor-manifest.json') filename: '[name]_[hash].js',
debug: true,
context: path.join(__dirname, '..'),
entry: {
vendor: [...config.vendor, 'babel-polyfill', 'bootstrap-sass', 'font-awesome']
}
}), }),
new HardSourceWebpackPlugin({
environmentHash: {
root: process.cwd(),
directories: ['webpack_config'],
files: ['package.json']
}
}),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development') 'process.env.NODE_ENV': JSON.stringify('development')
}), }),

View File

@ -1,31 +0,0 @@
const path = require('path');
const webpack = require('webpack');
const config = require('./config');
const _ = require('./utils');
module.exports = {
entry: {
vendor: [path.join(__dirname, '../common', 'vendors.js')]
},
output: {
path: path.join(__dirname, '../static'),
filename: 'dll.[name].js',
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, '../dll', '[name]-manifest.json'),
name: '[name]',
context: path.resolve(__dirname, '../common')
})
],
resolve: {
modules: [
// places where to search for required modules
'node_modules',
config.srcPath,
_.cwd('node_modules'),
_.cwd('./')
]
}
};

View File

@ -8,8 +8,8 @@ const freezerConfig = Object.assign({}, baseConfig, {
target: undefined, target: undefined,
performance: undefined, performance: undefined,
module: { module: {
// Typescript loader // Typescript loader
loaders: [baseConfig.module.loaders[0]] loaders: [baseConfig.module.rules[0]]
}, },
// Point at freezer, make sure it's setup to run in node // Point at freezer, make sure it's setup to run in node

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ProgressPlugin = require('webpack/lib/ProgressPlugin'); const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@ -14,8 +15,8 @@ const distFolder = 'dist/';
// Clear out build folder // Clear out build folder
rimraf.sync(distFolder, { rmdirSync: true }); rimraf.sync(distFolder, { rmdirSync: true });
base.devtool = 'source-map'; base.devtool = false;
base.module.loaders.push( base.module.rules.push(
{ {
test: /\.css$/, test: /\.css$/,
use: ExtractTextPlugin.extract({ use: ExtractTextPlugin.extract({
@ -43,15 +44,24 @@ base.entry.vendor = config.vendor;
// use hash filename to support long-term caching // use hash filename to support long-term caching
base.output.filename = '[name].[chunkhash:8].js'; base.output.filename = '[name].[chunkhash:8].js';
// add webpack plugins // add webpack plugins
base.plugins.unshift(
new FaviconsWebpackPlugin({
logo: path.resolve(__dirname, '../static/favicon/android-chrome-384x384.png'),
background: '#163151',
inject: true
})
);
base.plugins.push( base.plugins.push(
new ProgressPlugin(), new ProgressPlugin(),
new ExtractTextPlugin('[name].[chunkhash:8].css'), new ExtractTextPlugin('[name].[chunkhash:8].css'),
new webpack.DefinePlugin({
'process.env.BUILD_DOWNLOADABLE': JSON.stringify(!!process.env.BUILD_DOWNLOADABLE)
}),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production') 'process.env.NODE_ENV': JSON.stringify('production')
}), }),
new BabelMinifyPlugin(undefined, { new BabelMinifyPlugin(),
comments: false
}),
// extract vendor chunks // extract vendor chunks
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', name: 'vendor',