Webpack Tips and Tricks

Cover Image for Webpack Tips and Tricks

Over the last few months I've been delving deeper into the world of Webpack.

Below I'll demonstrate a few cool tricks that I've learned that really isn't as obvious as it should be, which I believe any dev that using Webpack will encounter at some stage or another.

Context: I'm using Webpack for an Angular2 project, thus I'm using TypeScript, and some of the examples are pertaining to TypeScript only solutions.

How to: Serve 3rd party JavaScript libraries

that do not have TypeScript wrapped modules (yet)... if working in TypeScript

Install the 3rd party module via npm, then expose it via an alias:

webpack.conf.js:

module.exports = {
module: {
resolve: {
alias: {
moment$: path.join(__dirname,'../node_modules/moment')
}
}
}
}

Somewhere in your app

import * as moment from 'moment/moment';


How to: Import Bootstrap and FontAwesome.

There are 2 amazing OpenSource projects:
bootstrap-loader and font-awesome-loader, both made by the ShakaCode team.

To implement them, install via npm, and add the following to your Webpack entry point:

webpack.conf.js:

  entry: {
vendor: [
'font-awesome-loader',
bootstrapEntryPoints.dev // bootstrapEntryPoints.js
]
}

Then setup a helper service or point to the config path manually:

bootstrapEntryPoints.js:

'use strict';

require('dotenv').config();
const fs = require('fs');

function getBootstraprcCustomLocation() {
return process.env.BOOTSTRAPRC_LOCATION;
}

const bootstraprcCustomLocation = getBootstraprcCustomLocation();

let defaultBootstraprcFileExists;

try {
fs.statSync('./.bootstraprc');
defaultBootstraprcFileExists = true;
} catch (e) {
defaultBootstraprcFileExists = false;
}

if (!bootstraprcCustomLocation && !defaultBootstraprcFileExists) {

/* eslint no-console: 0 */
console.log('You did not specify a \'bootstraprc-location\' '+'arg or a ./.boostraprc file in the root.');

console.log('Using the bootstrap-loader default configuration.');
}

// DEV and PROD have slightly different configurations

let bootstrapEntryPoint;

if (bootstraprcCustomLocation) {

bootstrapDevEntryPoint = 'bootstraploader/lib/bootstrap.loader?' +

`configFilePath=${__dirname}/${bootstraprcCustomLocation}` +
'!bootstrap-loader/no-op.js'; // for prod add loader?extractStyles

} else {
bootstrapDevEntryPoint = 'bootstrap-loader'; // for prod replace with 'bootstrap-loader/extractStyles'
}

module.exports = {
dev: bootstrapEntryPoint
};

...and add a *.bootstraprc* file. This will allow you to manually configure and cherry pick features for your bootstap build.

.bootstraprc:

# Output debugging info
# loglevel: debug
# Major version of Bootstrap: 3 or 4

bootstrapVersion: 3

# If Bootstrap version 3 is used - turn on/off custom icon font path
useCustomIconFontPath: false

# Webpack loaders, order matters

styleLoaders:
- style-loader
- css-loader
- sass-loader

# Extract styles to stand-alone css file
# Different settings for different environments can be used,
# It depends on value of NODE_ENV environment variable
# This param can also be set in webpack config:
# entry: 'bootstrap-loader/extractStyles'
# extractStyles: false
# env:
# development:
# extractStyles: false
# production:
# extractStyles: true

# Customize Bootstrap variables that get imported before the original Bootstrap variables.
# Thus original Bootstrap variables can depend on values from here. All the bootstrap
# variables are configured with !default, and thus, if you define the variable here, then
# that value is used, rather than the default. However, many bootstrap variables are derived
# from other bootstrap variables, and thus, you want to set this up before we load the

# official bootstrap versions.
# For example, _variables.scss contains:
# $input-color: $gray !default;
# This means you can define $input-color before we load _variables.scss

# preBootstrapCustomizations: ./src/scss/bootstrap/pre-customizations.scss
# This gets loaded after bootstrap/variables is loaded and before bootstrap is loaded.
# A good example of this is when you want to override a bootstrap variable to be based
# on the default value of bootstrap. This is pretty specialized case. Thus, you normally
# just override bootrap variables in preBootstrapCustomizations so that derived
# variables will use your definition.

#
# For example, in _variables.scss:
# $input-height: (($font-size-base * $line-height) + ($input-padding-y * 2) + ($border-width * 2)) !default;
# This means that you could define this yourself in preBootstrapCustomizations. Or you can do

# this in bootstrapCustomizations to make the input height 10% bigger than the default calculation.
# Thus you can leverage the default calculations.
# $input-height: $input-height * 1.10;
# bootstrapCustomizations: ./src/scss/bootstrap/customizations.scss

# Import your custom styles here. You have access to all the bootstrap variables. If you require

# your sass files separately, you will not have access to the bootstrap variables, mixins, clases, etc.

# Usually this endpoint-file contains list of @imports of your application styles.

appStyles:

# ./src/scss/_custom.scss

### Bootstrap styles

styles:

# Mixins
mixins: true

# Reset and dependencies
normalize: true

print: false

glyphicons: true

# Core CSS

scaffolding: true
type: true
code: true
grid: true
tables: true
forms: true
buttons: true

# Components
component-animations: true
dropdowns: true
button-groups: true
input-groups: true
navs: true
navbar: true
breadcrumbs: true
pagination: true
pager: true
labels: true
badges: true
jumbotron: true
thumbnails: true
alerts: true
progress-bars: true
media: true
list-group: true
panels: true
wells: true
responsive-embed: true
close: true

# Components w/ JavaScript
modals: true
tooltip: true
popovers: true
carousel: true

# Utility classes
utilities: true
responsive-utilities: true

### Bootstrap scripts

scripts:
transition: true
alert: true
button: true
carousel: true
collapse: true
dropdown: true
modal: true
tooltip: true
popover: true
scrollspy: true
tab: true
affix: true


How to: Pass environmental variables.

...like API paths, or environment specific static properties

Webpack had various ways of doing this but the route that worked the best for myself was to have each environment have its own .env file, and have Webpack map the properties inside the .env to its appropriate property within the app.

webpack.conf.js:

// npm install dotenv --save-dev
// dotenv is a super simple way to access .env files

require('dotenv').config();

new webpack.DefinePlugin({
"process.env": {
// process.env in this instance
// does not equal the runtime process.env of the app.
"KEY": process.env.KEY,
"FOO": {
// think of Webpack and the running
// application of having 2 separate contexts
"BAR": "yolo",
"BOOLEAN_PROPERTY": true
}
}
})


How to: Make your build faster.

Once your solution become more sizeable you might notice that it can get a bit slower from time to time, especially when using something like TypeScript. The below are a few options that you can make use of, depending on your use case.

* Use HappyPack to multithread certain loaders.

webpack.conf.js:

var HappyPack = require('happypack');

var happyThreadPool = HappyPack.ThreadPool({size: 5});

module.exports = {
module: {
loaders: [
{
test: /.html$/,
use: ['happypack/loader?id=html'], // <---- loader?id=BUNDLE_ID
exclude: /node_modules/
}
],
plugins: [
new HappyPack({
id: 'html', // <----- BUNDLE_ID
threadPool: happyThreadPool,
loaders: ['html-loader'] // loaders for this specific bundle
})
}
}

For TypeScript:

* Have specific dev build options to toggle source maps on or off (depeding on the task at hand)

webpack.conf.js:

// eval === fast, but not very useful, source-maps === useful, but slower (in larger projects)
devtool: process.env.ENVIRONMENT === 'dev' ? 'eval' : 'source-maps'

* Set transpileOnly to true. If you want to speed up compilation a lot you can use this option, but you end up losing a lot of benefits. docs

webpack.conf.js:

 loaders: [
{
test: /\.ts?$/,
loader: 'ts-loader?' + JSON.stringify({
transpileOnly: true
})
}
]

More Stories

Cover Image for Guitar Heroes: Monuments

Guitar Heroes: Monuments

Holy hell. What and experience!

Cover Image for Hello 3.0 World

Hello 3.0 World

Hello 3.0 World