Spring Sale! Save 30% on all books w/ code: PLANET24
Web Dev + WordPress + Security

Basic Webpack Setup

Preparing for Gutenberg, I’ve been sharpening my JavaScript skillz. Getting further into things like Webpack, React, Node.js, and all sort of other awesome scripting adventures. As I dive further into JavaScript land, it’s amazing how much more quickly I am able to do things that I never had to do before relying so heavily on JavaScript. Even so, the extra tools are good to have in the ’ol developer tool belt, so I’ll be sharing much more about JavaScript stuff in the months and maybe even years ahead. This post for example, is a walk-through of a basic webpack setup. Oooh, package management and dependency management.. I know, how exciting :)

Intro

This tutorial is aimed at folks already familiar with the basics of webpack, react, and other modern JavaScript tools. It’s meant as a guide and/or starting point to help people develop their own custom webpack configuratons. If you are new to webpack, etc., there are plenty of great tutorials on the Web to get you going. So with that out of the way, let’s dive in to the webpack fun zone.

Step 1: package.json

Requirements: Node (e.g., node, npm, et al)

First, set up the required directory/files:

/project/webpack.config.js
/project/src/index.js

Fine to keep both of these files blank for now.

Next, generate package.json:

npm init -y

That will give you a generic package.json file. Customize as desired. To give you an idea, here is my default template:

{
  "name": "project-name",
  "version": "0.0.0",
  "private": true,
  "description": "Tutorial @ https://perishablepress.com/basic-webpack-setup/",
  "main": "lib/index.js",
  "scripts": {
    "dev":  "webpack --mode development",
    "prod": "webpack --mode production",
  },
  "keywords": [
    "WordPress",
    "plugin"
  ],
  "author": "Jeff Starr https://monzillamedia.com/",
  "license": "GPL",
  "bugs": {
    "url": "https://perishablepress.com/contact/"
  },
  "homepage": "https://plugin-planet.com/",
  "dependencies": {},
  "devDependencies": {
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14",
  }
}

Note: you can define the package configuration via .npmrc file, for example:

cache-min=99999999
progress=false
save=true
save-exact=true
package-lock=false
engine-strict=true
registry=http://registry.npmjs.org/
message = "Version %s"

Step 2: webpack and webpack-cli

With package.json set up, we can install webpack and webpack-cli:

npm install webpack webpack-cli --save-dev

In addition to installing webpack and webpack-cli, this command results in the following:

  • Adds install directory, /node_modules/
  • Installs webpack
  • Installs webpack-cli
  • Updates package.json with a section called devDependencies
  • Adds file, package-lock.json

Note: --save-dev saves as development dependency; --save saves as an application dependency. --save installs and writes to package.json dependencies field; --save-dev writes to devDependencies.

Testing the output

To view future generated webpack output in a browser, we can use html-webpack-plugin.

npm install html-webpack-plugin --save-dev

Then in webpack.config.js:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: "Project Name",
    }),
  ],
};

With this enabled, webkit will auto-generate an index.html file (added to the output directory) that includes the packaged JavaScript. So you can test in browsers, et al.

Note: the html-webpack-plugin can be customized, and of course there are a million alternatives, like mini-html-webpack-plugin, et al.

Further configuration

While we’re in webpack.config.js, let’s add some additional configuration:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: ['./src/index.js'],
  output: {
    filename: 'project-name.js',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: 'inline-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      title: "Project Name",
    }),
  ],
};

We’ve now added entry, output, and devtool to our exports. These are not required, but they enable us to customize the default webpack values.

Webpack Server

You can also implement watch mode and automatic browser refresh using webpack-dev-server:

npm install webpack-dev-server --save-dev

Then update package.json:

.
.
"scripts": {
    "dev":  "webpack --mode development",
    "prod": "webpack --mode production",
    "serv": "webpack-dev-server --mode development --open"
  },
.
.

Now run npm run serv and visit http://localhost:8080 to view the results. With the server running, any changes to the src code will reflect in terminal and reload the browser. To quit the server, press Ctrl+C.

To configure the server, add devServer via webpack.config.js, for example:

module.exports = {
  entry: ['./src/index.js'],
  output: {
    filename: 'project-name.js',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: 'inline-source-map',
  devServer: {
    stats: "errors-only",
    host: process.env.HOST, // default: localhost
    port: process.env.PORT, // default: 8080
    open: true, // open page in browser
    overlay: true, // error overlay
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "Project Name",
    }),
  ],
};

To go further, we can improve error display with error-overlay-webpack-plugin:

npm install error-overlay-webpack-plugin --save-dev

Then update webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ErrorOverlayPlugin = require('error-overlay-webpack-plugin');

module.exports = {
  entry: ['./src/index.js'],
  output: {
    filename: 'project-name.js',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: 'inline-source-map',
  devServer: {
    stats: "errors-only",
    host: process.env.HOST, // default: localhost
    port: process.env.PORT, // default: 8080
    open: true, // open page in browser
    overlay: true, // error overlay
  },
  plugins: [
    new ErrorOverlayPlugin(),
    new HtmlWebpackPlugin({
      title: "Project Name",
    }),
  ],
};

Bonus: Automate server restart after modifying webpack config:

npm install nodemon --save-dev

Then update package.json:

.
.
  "scripts": {
    "dev": "webpack --mode development",
    "prod": "webpack --mode production",
    "serv": "nodemon --watch webpack.config.js --exec \"webpack-dev-server --mode development --open\""
  },
.
.

With this in place, any changes to webpack.config.js will result in recompile, reload, and terminal infos. Try it and see!

Streamline Configuration

At this point you have a fully functional webpack setup. Now let’s go further by enhancing and streamlining webpack configuration. The key to extreme config powers is the webpack-merge plugin. So let’s start there..

Install webpack-merge:

npm install webpack-merge --save-dev

Then create webpack.parts.js and move/offload some code from the main config file, for example:

exports.devParts = ({ host, port, path } = {}) => ({
  entry: ['./src/index.js'],
  output: {
    filename: 'project-name.js',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: 'inline-source-map',
  devServer: {
    stats: "errors-only",
    host: process.env.HOST, // default: localhost
    port: process.env.PORT, // default: 8080
    open: true, // open page in browser
    overlay: true, // error overlay
  },
});

And we have this in webpack.config.js:

const path = require('path');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ErrorOverlayPlugin = require('error-overlay-webpack-plugin');
const parts = require('./webpack.parts');

process.on('unhandledRejection', err => {
	throw err;
});

const commonConfig = merge([]);

const productionConfig = merge([
  {
    entry: ['./src/index.js'],
  },
  {
    output: {
      filename: 'project-name.min.js',
      path: path.resolve(__dirname, 'dist')
    },
  }
]);

const developmentConfig = merge([
  {
    plugins: [
      new ErrorOverlayPlugin(),
      new HtmlWebpackPlugin({
        title: "Project Name",
      }),
    ],
  },
  parts.devParts({
    host: process.env.HOST,
    port: process.env.PORT,
    path: path,
  }),
]);

module.exports = mode => {
  if (mode === 'production') {
    return merge(commonConfig, productionConfig, { mode });
  }
  return merge(commonConfig, developmentConfig, { mode });
};

Last but not least in package.json, change mode to env:

"scripts": {
    "start": "nodemon --watch webpack.config.js --exec \"webpack-dev-server --env development\"",
    "build": "webpack --env production"
  },

That last step may not be necessary in Webpack 4, which seems to work with either mode or env. Any feedback on this appreciated :)

Streamlining configuration in this fashion affords greater flexibility and modular code, so you can customize, extend and recycle configs with ease. Moving discrete sections of code into their own files opens up the file structure and mnakes conditional configurations a breeze.

Add a project..

Once everything is set up, we can add a project and have some fun. For example, to add a project named something creative like project-name, we create the directory:

/project/src/project-name/index.js

Then in /project/src/index.js, edit the path to match the location:

import './project-name/index.js';

And we’re ready to begin adding scripts to our project.

Note: when running webpack, can use --devtool false to make the dev output easier to read.

L8R

Hopefully this brisk tutorial has been useful. I’m still very much a beginner when it comes to JavaScript, but I’ve been using it in my projects (in varying degrees) for 12+ years now. So please let me know if anything is off-track, unfocused, or just sheer and utter buffoonery. You can leave a comment below, or reach me anytime via contact form. Thanks!

Next..

Note to self: In a future post, we’ll continue with how to add CSS to webpack. As with this post, they will be based on my own notes and demos created while learning how to use webpack :)

About the Author
Jeff Starr = Web Developer. Book Author. Secretly Important.
Wizard’s SQL for WordPress: Over 300+ recipes! Check the Demo »
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
GA Pro: Add Google Analytics to WordPress like a pro.
Thoughts
I live right next door to the absolute loudest car in town. And the owner loves to drive it.
8G Firewall now out of beta testing, ready for use on production sites.
It's all about that ad revenue baby.
Note to self: encrypting 500 GB of data on my iMac takes around 8 hours.
Getting back into things after a bit of a break. Currently 7° F outside. Chillz.
2024 is going to make 2020 look like a vacation. Prepare accordingly.
First snow of the year :)
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.