import path from 'path'; import webpack from 'webpack'; import extend from 'extend'; import AssetsPlugin from 'assets-webpack-plugin'; const isDebug = !process.argv.includes('--release'); const isVerbose = process.argv.includes('--verbose'); // // Common configuration chunk to be used for both // client-side (client.js) and server-side (server.js) bundles // ----------------------------------------------------------------------------- const config = { context: path.resolve(__dirname, '../src'), output: { path: path.resolve(__dirname, '../build/public/assets'), publicPath: '/assets/', sourcePrefix: ' ', pathinfo: isVerbose, }, module: { loaders: [ { test: /\.jsx?$/, loader: 'babel-loader', include: [ path.resolve(__dirname, '../src'), ], query: { // https://github.com/babel/babel-loader#options cacheDirectory: isDebug, // https://babeljs.io/docs/usage/options/ babelrc: false, presets: [ // Latest stable ECMAScript features // https://github.com/babel/babel/tree/master/packages/babel-preset-latest 'latest', // Experimental ECMAScript proposals // https://github.com/babel/babel/tree/master/packages/babel-preset-stage-0 'stage-0', // JSX, Flow // https://github.com/babel/babel/tree/master/packages/babel-preset-react 'react', ...isDebug ? [] : [ // Optimize React code for the production build // https://github.com/thejameskyle/babel-react-optimize 'react-optimize', ], ], plugins: [ // Externalise references to helpers and builtins, // automatically polyfilling your code without polluting globals. // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-runtime 'transform-runtime', ...!isDebug ? [] : [ // Adds component stack to warning messages // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source 'transform-react-jsx-source', // Adds __self attribute to JSX which React will use for some warnings // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self 'transform-react-jsx-self', ], ], }, }, { test: /\.js$/, loader: 'babel', include: path.resolve(__dirname, './node_modules/react-icons/md'), query: { presets: ['latest', 'react'], }, }, { test: /\.css/, loaders: [ 'isomorphic-style-loader', `css-loader?${JSON.stringify({ // CSS Loader https://github.com/webpack/css-loader importLoaders: 1, sourceMap: isDebug, // CSS Modules https://github.com/css-modules/css-modules modules: true, localIdentName: isDebug ? '[name]-[local]-[hash:base64:5]' : '[hash:base64:5]', // CSS Nano http://cssnano.co/options/ minimize: !isDebug, discardComments: { removeAll: true }, })}`, 'postcss-loader?pack=default', ], }, { test: /\.md$/, loader: path.resolve(__dirname, './lib/markdown-loader.js'), }, { test: /\.json$/, loader: 'json-loader', }, { test: /\.txt$/, loader: 'raw-loader', }, { test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, loader: 'file-loader', query: { name: isDebug ? '[path][name].[ext]?[hash:8]' : '[hash:8].[ext]', }, }, { test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, loader: 'url-loader', query: { name: isDebug ? '[path][name].[ext]?[hash:8]' : '[hash:8].[ext]', limit: 10000, }, }, ], }, resolve: { root: path.resolve(__dirname, '../src'), modulesDirectories: ['node_modules'], extensions: ['', '.webpack.js', '.web.js', '.js', '.jsx', '.json'], }, // Don't attempt to continue if there are any errors. bail: !isDebug, cache: isDebug, debug: isDebug, stats: { colors: true, reasons: isDebug, hash: isVerbose, version: isVerbose, timings: true, chunks: isVerbose, chunkModules: isVerbose, cached: isVerbose, cachedAssets: isVerbose, }, // The list of plugins for PostCSS // https://github.com/postcss/postcss postcss(bundler) { return { default: [ // Transfer @import rule by inlining content, e.g. @import 'normalize.css' // https://github.com/jonathantneal/postcss-partial-import require('postcss-partial-import')({ addDependencyTo: bundler }), // Allow you to fix url() according to postcss to and/or from options // https://github.com/postcss/postcss-url require('postcss-url')(), // W3C variables, e.g. :root { --color: red; } div { background: var(--color); } // https://github.com/postcss/postcss-custom-properties require('postcss-custom-properties')(), // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em); // https://github.com/postcss/postcss-custom-media require('postcss-custom-media')(), // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { } // https://github.com/postcss/postcss-media-minmax require('postcss-media-minmax')(), // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6; // https://github.com/postcss/postcss-custom-selectors require('postcss-custom-selectors')(), // W3C calc() function, e.g. div { height: calc(100px - 2em); } // https://github.com/postcss/postcss-calc require('postcss-calc')(), // Allows you to nest one style rule inside another // https://github.com/jonathantneal/postcss-nesting require('postcss-nesting')(), // Unwraps nested rules like how Sass does it // https://github.com/postcss/postcss-nested require('postcss-nested')(), // W3C color() function, e.g. div { background: color(red alpha(90%)); } // https://github.com/postcss/postcss-color-function require('postcss-color-function')(), // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px); } // https://github.com/iamvdo/pleeease-filters require('pleeease-filters')(), // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100%; } // https://github.com/robwierzbowski/node-pixrem require('pixrem')(), // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { } // https://github.com/postcss/postcss-selector-matches require('postcss-selector-matches')(), // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors // https://github.com/postcss/postcss-selector-not require('postcss-selector-not')(), // Postcss flexbox bug fixer // https://github.com/luisrudge/postcss-flexbugs-fixes require('postcss-flexbugs-fixes')(), // Add vendor prefixes to CSS rules using values from caniuse.com // https://github.com/postcss/autoprefixer require('autoprefixer')({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], }), ], }; }, }; // // Configuration for the client-side bundle (client.js) // ----------------------------------------------------------------------------- const clientConfig = extend(true, {}, config, { entry: { client: './client.js', }, output: { filename: isDebug ? '[name].js' : '[name].[chunkhash:8].js', chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js', }, target: 'web', plugins: [ // Define free variables // https://webpack.github.io/docs/list-of-plugins.html#defineplugin new webpack.DefinePlugin({ 'process.env.NODE_ENV': isDebug ? '"development"' : '"production"', 'process.env.BROWSER': true, __DEV__: isDebug, }), // Emit a file with assets paths // https://github.com/sporto/assets-webpack-plugin#options new AssetsPlugin({ path: path.resolve(__dirname, '../build'), filename: 'assets.js', processOutput: x => `module.exports = ${JSON.stringify(x, null, 2)};`, }), // Move modules that occur in multiple entry chunks to a new entry chunk (the commons chunk). // http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: module => /node_modules/.test(module.resource), }), ...isDebug ? [] : [ // Assign the module and chunk ids by occurrence count // Consistent ordering of modules required if using any hashing ([hash] or [chunkhash]) // https://webpack.github.io/docs/list-of-plugins.html#occurrenceorderplugin new webpack.optimize.OccurrenceOrderPlugin(true), // Search for equal or similar files and deduplicate them in the output // https://webpack.github.io/docs/list-of-plugins.html#dedupeplugin new webpack.optimize.DedupePlugin(), // Minimize all JavaScript output of chunks // https://github.com/mishoo/UglifyJS2#compressor-options new webpack.optimize.UglifyJsPlugin({ compress: { screw_ie8: true, // React doesn't support IE8 warnings: isVerbose, unused: true, dead_code: true, }, mangle: { screw_ie8: true, }, output: { comments: false, screw_ie8: true, }, }), ], ], // Choose a developer tool to enhance debugging // http://webpack.github.io/docs/configuration.html#devtool devtool: isDebug ? 'cheap-module-source-map' : false, // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. // https://webpack.github.io/docs/configuration.html#node // https://github.com/webpack/node-libs-browser/tree/master/mock node: { fs: 'empty', net: 'empty', tls: 'empty', }, }); // // Configuration for the server-side bundle (server.js) // ----------------------------------------------------------------------------- const serverConfig = extend(true, {}, config, { entry: { server: './server.js', }, output: { filename: '../../server.js', libraryTarget: 'commonjs2', }, target: 'node', externals: [ /^\.\/assets$/, (context, request, callback) => { const isExternal = request.match(/^[@a-z][a-z/.\-0-9]*$/i) && !request.match(/\.(css|less|scss|sss)$/i); callback(null, Boolean(isExternal)); }, ], plugins: [ // Define free variables // https://webpack.github.io/docs/list-of-plugins.html#defineplugin new webpack.DefinePlugin({ 'process.env.NODE_ENV': isDebug ? '"development"' : '"production"', 'process.env.BROWSER': false, __DEV__: isDebug, }), // Do not create separate chunks of the server bundle // https://webpack.github.io/docs/list-of-plugins.html#limitchunkcountplugin new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }), // Adds a banner to the top of each generated chunk // https://webpack.github.io/docs/list-of-plugins.html#bannerplugin new webpack.BannerPlugin('require("source-map-support").install();', { raw: true, entryOnly: false }), ], node: { console: false, global: false, process: false, Buffer: false, __filename: false, __dirname: false, }, devtool: isDebug ? 'cheap-module-source-map' : 'source-map', }); export default [clientConfig, serverConfig];