Setting up a modern pipeline for HTML development with Gulp

23 Sep 2019 - John


Ok, maybe not development since we're just talking about HTML, but that is not an excuse to not take advantage of the many tools we have at our disposal.

In this post, I'll walk you through my process of setting up an HTML project with automated builds, SASS compilation, image minification and automatic browser reloads.

Why HTML?

I rarely jump straight into react and start coding. While things look good in Photoshop or Adobe XD, I like to get a feel of what the whole thing is going to look like, and if necessary, fix any roadblock that might not be that evident when writing the first components of an app. Also, if you're into WordPress development, having an HTML template of all your theme pages will speed up you development time drastically.

A foreword on Node.js and ES6

At the time of this writing, Node.js is built on V8, which still lacks certain ES6 features that we're going to have to plug through Babel in order to keep things working.

Let's dive in!

First create a new directory and cd into it:

mkdir html && cd html

Then run npm init and accept the default values. You should end up with a lonely package.json file. Let's give it some company.
Create a dist folder to receive our final files, as well as an src folder. Also, create an images folder inside each, as well as an scss folder in your source folder. I usually compile all of my SASS into one huge CSS file (I know) and I like it next to my index file, so I won't be creating a css folder, but feel free to create one:

html
├── dist
│   └── images
│   └── css (optional)
├── package.json
└── src
    ├── images
    └── scss

Back in our console, install all the node packages we need:

npm i --save-dev babel babel-core babel-preset-es2015 browser-sync gulp gulp-imagemin gulp-newer gulp-sass

This will download a lot of packages so go get yourself a warm mug of coffee. After it's done, you'll end up with a node_modules folder on your root directory. Let's keep going.

Create an index.htmldile inside the ./src folder and a styles.scss file in your ./src/scss folder.

Set up Babel

Create a file called .babelrcand add the following code:

{
  "plugins": ["transform-es2015-modules-commonjs"]
}

This will tell node not to freak out when we do ES6 style imports.

Finish setting up the main files

Finally, we need to link index.html file to our yet inexistent styles.css. This will be a bit confusing, but bear with me, it will all make sense in a minute. Add a link on the header of your ./src/index.html to whatever you named your scss file, but instead of using the .scss extension, we're going to use the .css one instead. The reason is you will be working on the ./src folder, but will be viewing your files on the ./dist folder. So if you named your SCSS file ./src/scss/my-awesome-styles.scss, the head section of your ./src/index.html file should have a line that looks something like this:

<link rel="stylesheet" href="my-awesome-styles.css">

Set up Gulp

Time for some magic! Create one last file called gulpfile.babel.js in the root of your project. I also added a readme file and a .gitignore file:

html
├── dist
│   ├── images
│   ├── css (optional - I'm not using it)
│   └── js (optional - I'm not using it)
├── .gitignore
├── gulpfile.babel.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── src
    ├── index.html
    └── scss
        └── styles.scss

In yor gulpfile, import all the requirements:

// gulpfile.babel.js
// All this will error out if you don't use Babel
import gulp from 'gulp';
import sass from 'gulp-sass';
import imagemin from "gulp-imagemin";
import browserSync from "browser-sync";
const sassOptions = {outputStyle: 'expanded', errLogToConsole: true};

Nothing out of the ordinary. We're just importing the modules we'll be using in our project:

  • Gulp is our main package and task runner.
  • Gulp-sass compiles our SASS files into CSS.
  • Gulp-imagemin will apply some seriously awesome compression to our images from ./src/images, which we'll later copy to our ./dist/images folder.
  • Finally, browserSync will reload the browser every time a change is detected.
  • The sassOptions line is a variable that we'll later use to tell SASS how we want our files compiled. Check their repo for more into more available options.

Our first task

We're almost there! Still inside our gulpfile, we'll set up our first task like this:

exports.sass = () => (
  gulp.src('./src/scss/styles.scss')
  .pipe(sass(sassOptions))
  .pipe(gulp.dest('./dist'))
  .pipe(browserSync.reload({stream: true}))
);

This is our sass task. It basically says:

  • Take whatever there is in our .scss file.
  • Apply the options we set in the previous step and compile.
  • Dump the resulting CSS file in our destination folder. In my case I used ./dist, but if you're working with more than one CSS file, you might want to use ./dist/css or something to that effect. Also, the resulting CSS file is the one we linked to in the ./src/index.html, the one that still didn't exist? Well here it is!
  • Finally, refresh the browser to check the changes.

Set up the rest of the tasks

The rest of the task are pretty much self explanatory. They take an input file, apply whatever task they do and copy them to the ./dist folder:

exports.images = () => (
  gulp.src('./src/images/**/*')
  .pipe(imagemin())
  .pipe(gulp.dest('./dist/images'))
  .pipe(browserSync.reload({stream: true}))
);
exports.copy = () => (
  gulp.src('./src/*.html')
  .pipe(gulp.dest('./dist'))
  .pipe(browserSync.reload({stream: true}))
);

Finally, set up the main task

If you try to run Gulp as it is right now, you might notice that it either errors out or does nothing. That's because we haven't set a watcher yet:

gulp.task('serve', () => {
  browserSync.init({
    server: {
      baseDir: './dist',
      index: 'index.html'
    },
  notify: false,
  injectChanges: true
});
gulp.watch('./src/scss/**/*', gulp.series('sass'));
gulp.watch('./src/images/**/*', gulp.series('images'));
gulp.watch('./src/*.html', gulp.series('copy'));
gulp.watch('./dist/*').on('change', browserSync.reload);
});
gulp.task('default', gulp.series('serve'));

The first bit of this block is the server setup. We're creating the task "serve" and telling it to load index.html from our ./dist folder, along with a couple of options you might want to toy with.

The second block is where the actual magic happens. As long as the Gulp server is running, it will be WATCHING for changes in the specified files and run the associated tasks.

Finally, the last line is where we register the "serve" task as the default one to run when we run Gulp.
At this point, your ./src/index.html file should look something like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="styles.css">
    <title>Test</title>
</head>
<body>
</body>
</html>

Go ahead and fire up the terminal and run gulp. If everything went well, you should see a blank page in your browser (if not, point it to http://localhost:3000).
Now go ahead and make some edits on your ./src/index.html or ./src/scss/styles.scss.

Credit: Saturday Night Life - NBC
Be sure to check out this repo I'm working on for a look at the complete code and don't forget to share!