Skip to content

Instantly share code, notes, and snippets.

@RichardBray
Last active November 3, 2022 18:51
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RichardBray/338c5f4220d615df1fd48cad1b6989e6 to your computer and use it in GitHub Desktop.
Save RichardBray/338c5f4220d615df1fd48cad1b6989e6 to your computer and use it in GitHub Desktop.
Moving from Gulp to Webpack

Moving from Gulp to webpack

I've been avoiding learning Webpack for a while now as I never thought I needed to learn another build tool, Gulp does everything I'd ever need from a build tool now or in the future. However, ever since we've moved from AngularJS to Angular (or Angular 2+) as well as introducing standards such as; TypeScript instead of Javascript and a Jasmine/Karma combo for UI testing, but also Webpack as an initial build tool. I've avoided it for long enough and now, in September 2017, I thought it's time to finally move on from my old friend Gulp.

Good Old Gulpfile

If you've never heard of Gulp before, this isn't the post to learn, there are plenty of good tutorials out there a Google search away. Then again, you don't really need to know Gulp to understand what's going on so feel free to continue reading nevertheless.

Here's what my old Gulpfile looks like. I've decided against putting it all into this post as it's quite long. It essentially does 5 thing;

  • Starts a webserver
  • Preprocesses scss to css
  • Merges and compresses js files
  • Moves and minifies html files
  • And compresses images

It's a pretty standard set of tasks for creating a basic static site. Although it's pretty unlikely that someone will go through the effort of applying webpack to a simple site, I will go through how to do all of this in said build tool as a kind of beginners guide to webpack, or a designers guide, however you see fit. I wouldn't advise running this, a lot of the plugins are very old and not only will you get a bunch of messages asking you to upgrade them, the file probably wouldn't work 😞

1. Create blank folders

Let's start from a blank slate here. Create six new folders in the hierarchy specified below.

gulp-to-webpack
├── src
│   ├── app
│   ├── assets
|   │   ├── img
|   │   ├── styles

I found out after wiriting this that the app folder is pretty pointless but it's not that big of a deal.

Create a file called index.html in the src folder with the following code;

  <!-- gulp-to-webpack/src/index.html -->

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Test Document</title>
  </head>
  <body>
    <h1>Test Doc</h1>
  </body>
  </html>

Create a file called app.js in the app file and add some simple code like;

  // gulp-to-webpack/src/app/app.js
  document.write("It works.");

2. Run Webpack dev Server

I remember listening to ShopTalk show a while ago and one of the hosts(Dave Rupert) joked it took around a day to set webpack up. Luckily nowadays it doesn't take that long and you should have it up and running in just a few minutes.

Open up a terminal/command prompt, cd into the gulp-to-webpack directory and run;

$ npm init -y

The -y answers yes to all the questions npm asks during setup. This creates a package.json file in the root directory.

Now install webpack and webpack-dev-server;

$ npm i --save-dev webpack webpack-dev-server

webpack-dev-server is similar to gulp-webserver in the sense that it creates a server to test on. It has a bunch of other advantages.

Open the package.json file and if it's not there already, add a new line in the scripts section to run the webpack-dev-server;

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "start": "webpack-dev-server",
},

This will allow us to start the server by typing npm start in the terminal, or yarn start if you have Yarn installed.

Create a file in the root directory called webpack.config.js and open it up. There are four main things a webpack config file should have; entry, output, module and plugins.

  • Entry is the main JS file that webpack will use.
  • Output is where what it will output all the content to.
  • Module all the rules that will be applied to your files.
  • Plugins where all the plugins get loaded.

In our case we'll just need Entry and Output from the four.

const path = require('path');

module.exports = {
  entry: './src/app/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

Okay I'll go through things line-by-line.

  1. const path = require('path'); We add a constant variable called path which comes default with Node.js. This is used to specify the the root folder in line 6.
  2. blank
  3. module.exports = { Is the way to export modules in Node.js so it can be used by other files. I'm not sure exaclty why webpack needs an export but I guess that's what makes it so magical.
  4. entry: './src/app/app.js', Our main js file. Most files in webpack go through this file so as you'll see later on, our css and any other js will need to be imported here.
  5. output: { Creates the output object.
  6. path: path.resolve(__dirname, 'dist'), The path we want our used assets and code to be go to. We use the path import from line 1 for path.resolve the we refer to the root directory with __dirname and we export our processed code to a file called dist.
  7. filename: 'bundle.js' The filename to export all the bundled JS to.

Okay, one more thing before we're good to go. Let's tell the dev server were our directory containing the index.html file is. Add this code to the bottom of the file above the last closing bracket };.

// gulp-to-webpack/webpack.config.js

devServer: {
  contentBase: './src'
}

Okay not run;

$ npm start

If everything went well, navigating to http://localhost:8080/ in your browser should show you this.

Note: Gone through the code and realised the html-loader is required before the js is loaded in automatically, so please ignore the image below until that is done.

first run

3. Adding some CSS

First let's create a CSS file. Make a new file in your assets > styles folder called styles.css and add the following code;

/* gulp-to-webpack/src/assets/styles/styles.css */

:root {
  --grey: #ccc;
}

body {
  background-color: var(--grey);
  font-family: sans-serif;
  font-size: 14px;
}

h1 {
  font-size: 10em;
}

Now let's import the css to our app.js file, add this line right to the top.

// gulp-to-webpack/src/app/app.js
import '../assets/styles/style.css';

So there are several ways this can be done;

I will run through both, but the latter takes a bit longer than the former.

a. Loading CSS with style-loader

Install both style-loader and css-loader

$ npm i --save-dev style-loader css-loader

Open up your webpack.config.js and create a module object with a rules array just below your output objct.

  // gulp-to-webpack/webpack.config.js

  entry: './src/app/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [

    ]
  }

Luckily the github pages on most of the webpack plugins and loaders have good examples in their documentation so it's simple to add a bunch in. I'll simplify their example a bit.

  // gulp-to-webpack/webpack.config.js

  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }

And that's it. It's important to make suer you're using arrays [] where they should be instead of objects {} I've made that mistake a few times before.

Webpack is esentially finding all the files in your code that end with .css, then executing actions from right to left on the next line. So in our case ['style-loader', 'css-loader'] css-loader gets the css first, then style loader places it in the head of our code.

Run npm start to see it in action.

Image 2

b. Loading CSS with extract-text-webpack-plugin

So if you've followed the steps in part a please comment out or delete that code in the webpack.config.js file. We'll keep the 'css-loader' as we'll need that for this method as well, go ahead and install extract-text-webpack-plugin.

$ npm i --save-dev extract-text-webpack-plugin

Now in the webpack.config.js add the following code below your const path line;

  // gulp-to-webpack/webpack.config.js
  const ExtractTextPlugin = require('extract-text-webpack-plugin');

Now create a new rule, again for css but written like so;

  // gulp-to-webpack/webpack.config.js
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          use: ['css-loader']
        })     
      }
    ]
  }

Here it's extracting all the code from files that end in .css using the css-loader. Now we need to tell it where to put all the code. Add a new plugins array above the devServer object, and below the module object.

  plugins: [
    new ExtractTextPlugin('style.css'),
  ],

Here we're exporting all our code to a file named 'style.css'. Once again, run npm start to see the changes.

c. Loading and minifying Scss

It's pretty simple from this point to export sass. Let's change our style.css to style.scss, remove all the code in the file and replace it with this;

  // gulp-to-webpack/src/assets/styles/style.scss

  $grey: #ccc;

  body {
    background-color: $grey;
  }

  h1 {
    font-size: 5em;
  }

Now update the style import in app.js and install sass-loader.

$ npm i --save-dev sass-loader

Let's add a new rule to our webpack.config.js file;

  // gulp-to-webpack/webpack.config.js
  rules: [
    {
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
        use: ['css-loader']
      })     
    },
    {
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
        use: ['css-loader', 'sass-loader']
      })     
    }    
  ]

Pretty simple right? Now let's ammend that rule to minimise our css.

  // gulp-to-webpack/webpack.config.js
  test: /\.scss$/,
  use: ExtractTextPlugin.extract({
    use: [
      {
        loader: 'css-loader',
        options: {
          minimize: true
        }
      },
      'sass-loader'
    ]
  })

As you can see, we've applied the minimisation option to the css-loader, to it is possible to minimise the code from section 3.a and b in this exact same way.

4. Merges and compress JS files

This is possibly the simplest thing we can do, all it requires is the addition of a plugin, and webpack does the rest.

Let's install the uglifyjs-webpack-plugin.

$ npm i --save-dev uglifyjs-webpack-plugin

Add it to the top of our webpack.config.js file, below the ExtractTextPlugin.

  // gulp-to-webpack/webpack.config.js
  const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

And let's scroll down to our plugins array and stick it in there too.

  // gulp-to-webpack/webpack.config.js
  plugins: [
    new UglifyJSPlugin(),
    new ExtractTextPlugin('style.css')
  ],

And that's it. As any js you use will have to be imported through the app.js file it'll compress right at the end.

5. Move and minifies HTML

I'll have to admit, I haven't yet tried this wth more than one Html file, but it shouldn't be too hard to figure out if that's a road you want to go down.

Again, it's another plugin install, just in case you hadn't figured it out. Let's install the html-webpack-plugin

$ npm i --save-dev html-webpack-plugin

Then the usual process for a plugin so a const at the top of the webpack.config.js file.

  // gulp-to-webpack/webpack.config.js
  const HtmlWebpackPlugin = require('html-webpack-plugin');

Now let's add some options for it. Below all the constants let's add a new object variable.

  // gulp-to-webpack/webpack.config.js
  let htmlOptions = {
    template: 'src/index.html',
    minify: {
      collapseWhitespace: true,
      removeAttributeQuotes: true
    }
  };

The options should be pretty self explanitory so I won't run through them, but there are a full list of minification options available on the HTMLMinifier github page which you can look at if you would like more information.

As for the moving of Html files or files in general that happens in memory each time you run the webpack server so you probably haven't noticed a physical file yet. That can be easily rectified. Let's add a new script command to the package.json file just below 'start', add;

  "build": "webpack",

So now running npm run build will build a dist folder in your root directory with all your compiled code.

a. Remove dist folder on new build

This is optional but let's say it provides, piece of mind. Whenver the build command is run you'll notice the dist folder stays there, there's no way of knowing if the files in it have been updated or not without manually checking it. One way to solve this is to delete the folder at the start of a build and replace it with the newly created file. That can be done with the clean-webpack-plugin.

The usually steps of installing the plugin and placing it at the opt of the webpack.config.js file apply here. However in the plugins array we'll need to place the file we want to remove on build like so.

  // gulp-to-webpack/webpack.config.js
  new CleanWebpackPlugin(['dist'])

And that's it, another simple plugin added. Now run a build and observe.

6. Compressing Images

Phew, we've got to the final point of this post, if you've gone through everything up to this point you've done incredibly well.

Compressing images will involve the installation of two loaders, file-loader and image-webpack-loader.

$ npm i --save-dev file-loader image-webpack-loader

Now let's add a new rule to the webpack.config.js file;

  // gulp-to-webpack/webpack.config.js
  {
    test: /\.(jpg|png|gif|svg)$/,
    use: [ 'file-loader', 'image-webpack-loader']
  }  

And the final step is to include an image tag in our Html. Open up the index.html file and add the following code below the h1 tag.

  <!-- gulp-to-webpack/src/index.html -->
  <h2>Extra Text</h2>
  <img src=<%=require("./assets/img/webpack.jpg")%> />

As you can see instead of requiring the image file at the top of the app.js file like the css we've required it in a specific location in the html so webpack know where to place the image in markup once it's done with it's compression. Now let's downlaod an image and save it in the assets > img folder. Say this one save it as webpack.jpg and place it in the relevant folder.

Now if you run the dev server and view the source, you'll notice the image isn't called 'webpack' but instead has a random alphanumeric string. That's because 'file-loader' by default will hash assets. To change that we'll have to add some options;

  // gulp-to-webpack/webpack.config.js
  {
    test: /\.(jpeg|png|gif|svg)$/,
    use: [
      {
        loader: 'file-loader',
        options: {
          name: '[name].[ext]'
        }
      }, 'image-webpack-loader'
    ]
  }

So now well use the actual name of the file, then it's extension. Run the dev server and you'll see that the naming is as it should be.

And that's it. Everyhting my gulp file could do, re-written in webpack. I hope this has been a helpful introduciton to those who have been wanting to move away from Gulp/Grunt and try webpack. The syntax is very different but once you get used to it, it makes sense. From this point onwards you can include a bunch of other loaders and plugins all with the same techniques listed above, and even go as far as having seperate webpack.config.js files for production, development, and tesitng. The possibilities are endless.

@yumyo
Copy link

yumyo commented Sep 25, 2017

Quite a number of missing or unclear steps. I managed to rebuild it in 30 minutes by trial and error. Probably worth linking a repo with the actual working files or fix the sequence of steps to avoid unnecessary headaches.

@RichardBray
Copy link
Author

@yumyo You're right I went through it quite recently on a fresh install and spotted a few issues. At the time of writing, I had some things installed already hence the mistakes. I'm hoping to fix the file at some point in the future, but for now, I have a repo here for it which might be a bit confusing but I guess it's better than nothing.

https://github.com/RichardBray/gulp-to-webpack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment