Skip to content

Instantly share code, notes, and snippets.

@andfaulkner
Last active April 4, 2024 12:43
Show Gist options
  • Save andfaulkner/8a6e9f911e89899d328c7ca91a0be267 to your computer and use it in GitHub Desktop.
Save andfaulkner/8a6e9f911e89899d328c7ca91a0be267 to your computer and use it in GitHub Desktop.
Split Webpack configuration into modules by functionality (rather than environment)
const prodExternals = {
'react': 'React',
'react-dom': 'ReactDOM',
'lodash': '_',
'redux': 'Redux',
'react-redux': 'ReactRedux',
'react-bootstrap': 'ReactBootstrap'
};
const testExternals = {
jsdom: 'window',
cheerio: 'window',
};
const devExternals = { };
const externals = {
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
};
module.exports = (NODE_ENV, TEST_ENV = false) => {
return Object.assign({},
externals,
(TEST_ENV === 'karma') ? testExternals: {},
(NODE_ENV === 'production') ? prodExternals : devExternals
);
};
const path = require('path');
const webpack = require('webpack');
// TODO fix the repetition here
const srcPath = path.join(__dirname, '..', 'client');
const loaders = [
{
test: /\.tsx?$/,
exclude: ['node_modules', '../node_modules'],
loaders: ['babel?presets[]=react&presets[]=es2015&presets[]=stage-0', 'ts-loader']
},
{
test: /\.((hbs)|(handlebars))$/,
loader: 'handlebars-loader'
},
{
test: /\.json/,
loader: 'json-loader'
},
{
test: /\.ya?ml$/,
exclude: ['node_modules', '../node_modules'],
loaders: ['yaml', 'json'],
},
{
test: /\.(jpg|png)$/,
loader: 'url?limit=25000',
include: path.join(srcPath, 'components')
},
{
test: /\.jsx?$/,
loader: 'babel',
exclude: /node_modules/
},
{
test: /\.css$/,
exclude: ['node_modules', '../node_modules', '../../node_modules'],
loader: "style-loader!css-loader!postcss-loader"
}
];
const devLoaders = [];
const loadersWithEnvSpecific = (process.env.NODE_ENV === 'production')
? loaders
: loaders.concat(devLoaders)
module.exports = loadersWithEnvSpecific;
const path = require('path');
const webpack = require('webpack');
const config = require("../config.js");
// Load required plugins
const HandlebarsPlugin = require('handlebars-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
// Get paths to important files
const buildPath = path.join(__dirname, '../..', 'build');
const buildFilePath = (currentPath) => path.join(buildPath, currentPath);
const srcPath = path.join(__dirname, '../..', 'client');
const srcFilePath = (currentPath) => path.join(srcPath, currentPath);
console.log('\n\nbuildFilePath: ', buildFilePath);
console.log('srcFilePath: ', srcFilePath, '\n\n');
/**
* Plugins shared by prod and dev
*/
const basePlugins = (NODE_ENV, LOG_LEVEL) => [
new ExtractTextPlugin("[name].css"),
new webpack.DefinePlugin({
NODE_ENV: JSON.stringify(NODE_ENV),
LOG_LEVEL: JSON.stringify(LOG_LEVEL),
'process.env': {
NODE_ENV: JSON.stringify(NODE_ENV),
LOG_LEVEL: JSON.stringify(LOG_LEVEL)
}
}),
new HandlebarsPlugin({
entry: srcFilePath("index.hbs"), // path to main hbs template
output: buildFilePath((NODE_ENV === 'development') ? "index.html" : "public/index.html"), // filepath to result
// output: buildFilePath("index.html"), // << ORIGINAL
data: config.indexHtmlData // data passed to main hbs template
})
];
/**
* Production-only Webpack plugins
*/
const prodPlugins = [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
drop_console: true,
drop_debugger: true,
dead_code: true,
unused: true,
evaluate: true,
if_return: true,
join_vars: true,
},
sourceMap: false,
}),
new LodashModuleReplacementPlugin({
chaining: true,
collections: true,
paths: true,
flattening: true,
caching: true,
currying: true,
placeholders: true
}),
new webpack.optimize.DedupePlugin()
];
/**
* @EXPORT
*/
module.exports = (NODE_ENV = 'development', LOG_LEVEL = 'verbose') => {
const plugins = basePlugins(NODE_ENV, LOG_LEVEL)
.concat((NODE_ENV === 'production') ? prodPlugins : []);
if (LOG_LEVEL === 'verbose' || LOG_LEVEL === 'silly') {
console.log('\n\nPLUGINS:\n', plugins, 'END PLUGINS\n\n');
}
return plugins;
};
const path = require('path');
const webpack = require('webpack');
module.exports = (webpack) => {
return [
// !------ ALREADY INCLUDED BY cssnext: require('autoprefixer'),
require('postcss-import')({
addDependencyTo: webpack
}),
require('postcss-env-replace')({
environment: process.env.NODE_ENV || 'development',
replacements: {
BASE_URL: {
production: '/assets/img',
development: 'http://localhost:8080/assets/img',
}
}
}),
require('postcss-mixins')({
mixinsFiles: path.join(__dirname, '../..', 'client/styles/mixins.css')
}),
require('postcss-partial-import')({
addDependencyTo: webpack
}),
require('postcss-inherit'),
require('postcss-each'),
require('postcss-inline-comment'), // allows '//' comments -- OK
require('postcss-flexibility'), // flexbox -- OK
require('lost'), // grids -- OK
require('postcss-nesting'),
require('postcss-simple-vars'),
require('postcss-utilities'), // see https://ismamz.github.io/postcss-utilities/docs
require('postcss-cssnext'),
require('postcss-calc')
];
}
/********************************** THIRD-PARTY UTILITY MODULES ***********************************/
const path = require('path');
const webpack = require('webpack');
const _ = require('lodash');
/**************************************** ENVIRONMENT VARS ****************************************/
// Extract environment vars from process.env, with fallbacks if expected vars not present
var NODE_ENV = process.env.NODE_ENV || 'development';
var TEST_ENV = process.env.TEST_ENV || false;
var LOG_LEVEL = process.env.LOG_LEVEL || 'verbose';
var WATCH = !_.includes(process.argv, '--block-watch');
// Detect if verbose mode is active
const isVerbose = (LOG_LEVEL === 'verbose') || (LOG_LEVEL === 'silly') ||
_.includes(process.argv, '--verbose');
// Display current environment ('development' or 'production')
console.log('\nprocess.env.NODE_ENV: ', NODE_ENV, '\n process.env.TEST_ENV: ', TEST_ENV, '\n');
// SET THE BABEL ENVIRONMENT (for use under the hood in transpiling)
const TARGET = process.env.npm_lifecycle_event;
process.env.BABEL_ENV = TARGET;
/************************************* WEBPACK CONFIG OPTIONS *************************************/
const config = require('./config');
const loaders = require('./webpack-modules/loaders-webpack.config');
const postCSSConfig = require('./webpack-modules/postcss-webpack.config');
const pluginsConfig = require('./webpack-modules/plugins-webpack.config')(NODE_ENV, LOG_LEVEL);
const externalsConfig = require('./webpack-modules/externals-webpack.config')(NODE_ENV, TEST_ENV);
/**************************************** DIRECTORY PATHS *****************************************/
// common paths
const srcPath = path.join(__dirname, '..', 'client');
const buildPath = path.join(__dirname, '..', 'build');
const publicBuildPath = path.join(__dirname, '..', 'build/public');
const libPaths = [path.join(__dirname, '..', 'node_modules')];
const srcFilePath = (currentPath) => path.join(srcPath, currentPath);
/****************************************** LOG CONFIGS *******************************************/
if (isVerbose) {
console.log('\nwebpack.dev.config.js:: OUTPUT CONFIG INFO: ');
console.log('\nloaders:\n', loaders, '\n');
console.log('\npostCSSConfig:\n', postCSSConfig, '\n');
console.log('\npluginsConfig:\n', pluginsConfig, '\n');
console.log('\nexternalsConfig:\n', externalsConfig, '\n');
}
/********************** WEBPACK DEVELOPMENT CONFIG (MERGES INTO BASE CONFIG) **********************/
/**
* Development-specific webpack configuration (settings of keys not provided to the
* production config, and keys with little config overlap between dev and prod).
* @type {Object}
*/
const webpackDevOptions = {
devtool: 'inline-source-map',
devServer: {
contentBase: buildPath,
progress: true,
colors: true,
port: config.port.WEBPACK_DEV_SERVER_PORT,
inline: true,
progress: true,
hot: true,
},
port: config.port.WEBPACK_DEV_SERVER_PORT,
output: {
path: buildPath,
filename: "app.js",
},
};
/********************** WEBPACK PRODUCTION CONFIG (MERGES INTO BASE CONFIG) ***********************/
/**
* Production-specific webpack configuration
* @type {Object}
*/
const webpackProdOptions = {
output: {
path: publicBuildPath,
publicPath: "/",
filename: "app.js",
},
};
/*********************************** BASE WEBPACK CONFIG OBJECT ***********************************/
/**
* Actual webpack config object.
* Environment-specific Webpack settings get merged into this base object.
*/
const webpackConfig = Object.assign({},
{
colors: true,
pathinfo: true,
progress: true,
watch: ((NODE_ENV === 'development') && WATCH),
context: path.join(__dirname, '..', '/client'),
entry: srcFilePath('root.tsx'),
externals: externalsConfig,
module: {
loaders,
noParse: [
/node_modules\/sinon/,
/client\/vendor\/jsonpath/
],
},
plugins: pluginsConfig,
postcss: postCSSConfig,
resolve: {
modulesDirectories: [
"./client",
"../node_modules",
"../node_modules/babel",
"./node_modules",
"./node_modules/babel"
],
extensions: ['', '.js', '.jsx', '.tsx', '.ts', '.css']
},
resolveLoader: { root: libPaths },
},
// ENVIRONMENT SPECIFIC BUILD OPTIONS
(NODE_ENV === 'development') ? webpackDevOptions : webpackProdOptions
(TEST_ENV === 'karma') ? { devtool: 'inline-source-map' } : {}
);
if (TEST_ENV === 'karma') {
webpackConfig.resolve.alias = Object.assign({},
webpackConfig.resolve.alias || {},
{ sinon: require.resolve('sinon/pkg/sinon') }
);
}
/********************************************* EXPORT *********************************************/
if (isVerbose) {
console.log('WEBPACK FINAL CONFIG OPTIONS: ', webpackConfig)
}
module.exports = webpackConfig;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment