Skip to content

Instantly share code, notes, and snippets.

@dtothefp
Last active April 21, 2018 01:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dtothefp/7769a27293cf771dbd60fe46dcc99c3c to your computer and use it in GitHub Desktop.
Save dtothefp/7769a27293cf771dbd60fe46dcc99c3c to your computer and use it in GitHub Desktop.
import path from 'path';
import webpack from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import UglifyJsPlugin from 'uglifyjs-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import Dotenv from 'dotenv-webpack';
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
import { BundleStatsPlugin } from './plugins/BundleStatsPlugin';
import { LoadableStatsPlugin } from './plugins/LoadableStatsPlugin';
import generateStats from './generate-stats';
import statsOptions from './stats-options';
import svgoLoader from './loaders/svgo-loader';
import defineEnv from './define-env-config';
import { babelLoaderOptions } from './loaders/babel-loader';
/**
* This is the Webpack configuration file for local development. It contains
* local-specific configuration such as the React Hot Loader, as well as:
*
* - The entry point of the application
* - Where the output file should be
* - Which loaders to use on what files to properly transpile the source
*
* For more information, see: http://webpack.github.io/docs/configuration.html
* @param {Object} config global task config
* @return {Object} webpack client config
*/
export default function(config) {
const {
assetDest,
cssModuleTest,
cssGlobalTest,
envVars,
src,
dest,
cwd,
base,
dllManifestFile,
experimental,
statOutput,
jsonDir,
makeConfigHash,
makeLoaders,
progressBar,
splitChunks,
excludeJsRe,
env,
quick,
library,
visualize,
chunkStatsFile,
stats,
entry: {js},
} = config;
const isDev = env === `development`;
const minimize = !isDev && !quick;
// TODO: add CDN path here depending upon TRAVIS_BRANCH
const publicPath = envVars.PUBLIC_PATH;
// TODO: webpack hashing is messed up, when it gets fixed remove this timestamp
const timestamp = new Date().getTime();
const assetEntries = {
css: {
development: `[name].css`,
production: `[name]-[chunkhash]-${timestamp}.css`,
},
js: {
development: `[name].js`,
production: `[name]-[chunkhash]-${timestamp}.js`,
},
};
const cssLoader = ({modules} = {}) => {
const options = {
importLoaders: 1,
minimize,
};
if (modules) {
Object.assign(options, {
modules: true,
localIdentName: `[name]__[local]___[hash:base64:5]`,
});
}
return {
loader: `css-loader`,
options,
};
};
const postcssLoader = {
loader: `postcss-loader`,
options: {
config: {
path: base(`postcss.config.js`),
},
},
};
const makeStyleLoaders = ({modules} = {}) => {
const loaders = [ cssLoader({modules}), postcssLoader ];
if (modules && isDev) {
loaders.unshift(`style-loader?singleton`);
}
return makeLoaders(loaders);
};
const devPlugins = [
new HardSourceWebpackPlugin({
configHash: makeConfigHash(),
environmentHash: {
root: process.cwd(),
directories: [],
files: [`yarn.lock`],
},
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.DllReferencePlugin({
manifest: cwd(dest, dllManifestFile),
}),
new webpack.WatchIgnorePlugin([
/node_modules/,
]),
];
const output = {
library,
libraryExport: `default`,
path: cwd(dest + assetDest),
filename: assetEntries.js[env],
publicPath,
};
const prodPlugins = [
// TEST: scope hoisting
// https://medium.com/webpack/brief-introduction-to-scope-hoisting-in-webpack-8435084c171f
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
];
const plugins = [
new Dotenv({
path: `./.env`,
safe: false,
}),
new webpack.DefinePlugin(
defineEnv({target: `client`, config})
),
new MiniCssExtractPlugin({
filename: assetEntries.css[env],
}),
progressBar,
];
const entry = [
`./${src}/${js}`,
];
if (visualize) {
plugins.push(
new BundleAnalyzerPlugin({statsOptions})
);
} else if (stats) {
plugins.push(
new BundleStatsPlugin({
filename: cwd(dest, `bundle-stats.json`),
})
);
} else {
plugins.push(...[
generateStats(config),
new LoadableStatsPlugin({
filename: cwd(dest, chunkStatsFile),
}),
]);
}
const extractLoader = MiniCssExtractPlugin.loader;
const extractCss = (loaders) => isDev ? loaders : [ extractLoader, ...loaders ];
const jsonExtractLoader = {
type: `javascript/auto`,
test: /\.json$/,
use: [
{
loader: path.resolve(__dirname, `loaders`, `json-loader.js`),
options: {
outputPath: cwd(dest, jsonDir),
name: `[path][name].[ext]`,
},
},
],
};
const devLoaders = [];
if (experimental) {
devLoaders.push(jsonExtractLoader);
}
const loaders = [
{
test: /\.jsx?$/,
exclude: /node_modules/,
enforce: `pre`,
use: makeLoaders({
loader: `eslint-loader`,
options: {
cache: true,
},
}),
},
{
test: /\.jsx?$/,
exclude: excludeJsRe,
use: makeLoaders({
loader: `babel-loader`,
options: babelLoaderOptions({env, target: `client`}),
}),
},
{
test: cssModuleTest,
use: extractCss(makeStyleLoaders({modules: true})),
},
{
test: cssGlobalTest,
use: [ extractLoader, ...makeStyleLoaders() ],
},
{
test: /\/images\/.*\.svg$/,
exclude: /assets-spa\/images\/.*\.svg$/,
use: [
`file-loader`,
svgoLoader(`file`),
],
},
{
test: /\.svg$/,
exclude: /\/images\/.*\.svg$/,
use: [
`svg-react-loader`,
svgoLoader(`inline`),
],
},
];
let devtool;
if (isDev) {
plugins.unshift(...devPlugins);
loaders.push(...devLoaders);
devtool = `cheap-module-eval-source-map`;
} else {
plugins.push(...prodPlugins);
devtool = `source-map`;
}
return {
devtool,
entry: {
main: entry,
},
output,
resolve: {
extensions: [ `.js`, `.jsx`, `.css` ],
alias: {
actions: cwd(src, `actions`),
analytics: cwd(src, `analytics`),
classnames: `classnames`,
pages: cwd(src, `pages`),
react: `react`,
'react-redux': `react-redux`,
utils: cwd(src, `utils`),
sheets: cwd(`node_modules`, `sheets`, `lib`),
},
modules: [
cwd(src),
base(`node_modules`),
`node_modules`,
],
},
mode: env,
performance: {
hints: false,
},
cache: true,
plugins,
module: {
rules: loaders,
},
optimization: {
minimize,
minimizer: [
new UglifyJsPlugin({
parallel: true,
sourceMap: true,
uglifyOptions: {
output: {
comments: false,
},
compress: {
warnings: false,
},
},
}),
],
splitChunks,
},
stats: isDev ? `none` : statOutput,
node: {
fs: `empty`,
__dirname: false,
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment