Gulp – Creating a Webpage

Gulp, Gulp – Creating a Webpage

Moving CSS and JavaScript files is all well and good, but we do actually want webpages right?

Let’s start our webpage generation by first moving the index.html file we created while learning more about streams.

It should look like:

/contents/index.html
1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
  <head>
    <title>Learning Gulp</title>
  </head>
  <body>
    <h1>Hello Gulp!</h1>
  </body>
</html>

We will then create a simple homepage task to move the index.html file to our build directory.

gulpfile.js
1
2
3
4
5
...
gulp.task("homepage", function() {
  return gulp.src("contents/index.html")
    .pipe(gulp.dest("build"));
});

Now test the task.

gulpfile.js
1
2
3
4
5
$ gulp homepage

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'homepage'...
Finished 'homepage' after 15 ms

It would be nice to be able to preview our website as we generate the content. Let’s do that next.

Gulp – Testing With Jasmine

Gulp, Gulp – Testing with Jasmine

You do test your JavaScript right? Well… you should and with gulp + Karma + Jasmine it is super easy.

First if you have not installed Karma and Jasmine, then do so now.

1
$ npm install karma-jasmine --save-dev

Next we will install the gulp-jasmine plugin for gulp.

1
$ npm install gulp-jasmine --save-dev

We can then create a test task to run all the specs found in a specs folder we will create.

gulpfile.js
1
2
3
4
5
6
var jasmine = require('gulp-jasmine');
...
gulp.task('specs', function () {
  return gulp.src('specs/**.js')
    .pipe(jasmine());
});

Let’s create a basic (failing) test to see that it is working.

/specs/our_test.js
1
2
3
4
5
describe('OMG a JavaScript Test', function() {
  it('should pass', function() {
    expect(true).toBe(false);
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ gulp specs

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'specs'...
F

Failures:

  1) OMG a JavaScript Test should pass
  1.1) Expected true to be false.

1 spec, 1 failure
Finished in 0.004 seconds
'specs' errored after 39 ms Tests failed

Now it is time to refactor. We will make the extremely difficult change from false to true to make our test pass.

/specs/our_test.js
1
2
3
4
5
describe('OMG a JavaScript Test', function() {
  it('should pass', function() {
    expect(true).toBe(true);
  });
});

And run our test suite again.

1
2
3
4
5
6
7
8
9
$ gulp specs

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'specs'...
.

1 spec, 0 failures
Finished in 0 seconds
Finished 'specs' after 39 ms

Next we will do is improve our testing task by adding a test-watch task to run as we edit our JavaScript files.

gulpfile.js
1
2
3
4
...
gulp.task('spec-watch', function() {
  gulp.watch(['specs/**.js', 'contents/javascripts/**.js'], ['test'])
});

Gulp – Uglify

Gulp, Gulp – Uglify

For JavaScript files we also want to uglify them. Uglifying JavaScript involves changing variable and function names to reduce their size. So a variable named customer might be renamed to x. JavaScript engines don’t care about descriptive names, only developers. So how do we uglify JavaScript files with gulp?

I know what you are going to say: “Blah, blah, blah… there is a plugin.” and you are correct.

1
$ npm install gulp-uglify --save-dev

For more information on gulp-uglify check out https://www.npmjs.org/package/gulp-uglify.

While we are uglifying the file, we will also concat all our JavaScript files together and move them to build/javascripts.

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
var uglify = require('gulp-uglify');
...

gulp.task('javascript', function () {
  console.log("Validate, Concat, Uglify, and Move all the javascript files");

  return gulp.src("contents/javascripts/**.js")
    .pipe(jsValidate())
    .on("error", notify.onError(function(error) {
      return error.message;
    }))
    .pipe(uglify())
    .pipe(concat('main.js'))
    .pipe(gulp.dest('build/javascripts'));
});

When you run our gulp javascript task now, we should see that our javascript files were uglified, concated, and moved to the build folder.

1
2
3
4
5
6
$ gulp javascript

Using gulpfile ~/YOUR_DIRECTORY/gulpFile.js
Starting 'javascript'...
Validate, Concat, Uglify, and Move all the javascript files
Finished 'javascript' after 55 ms

If you have an error here, be sure to check that your JavaScript is valid. Remember we were testing that last section.

The build script should create our /build/javascripts/main.js file.

/build/javascripts/main.js
1
function OMG(){var n=2;return n+10}

Gulp – Notify Pop Up

Gulp, Gulp – Notify Pop Up

In the previous article, we used gulp to validate our JavaScript. The error message would appear in the console. While this is awesome, there is a chance we could miss it.

Let’s use notifications to display a pop up window when we have a JavaScript error.

There is a gulp plugin to send notifications.

1
$ npm install gulp-notify --save-dev

For more information on gulp-notify check out https://www.npmjs.org/package/gulp-notify

Remember that gulp uses node’s streaming. It shouldn’t be a surprise that when gulp-jsvalidate finds an error, it emits an error event.

All we need to do is handle the event and use gulp-notify to send a notification with the error message.

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
var notify = require('gulp-notify');
...

...
gulp.task('javascript', function () {
  console.log("Validate JavaScript");
  return gulp.src("contents/javascripts/**.js")
    .pipe(jsValidate())
    .on("error", notify.onError(function(error) {
      return error.message;
    }));
});
...

Since our JavaScript is now valid, we need to make it invalid so we can see the error message.

/contests/javascript/somejs.js
1
2
3
4
function OMG() {
  var x * 2;
  return x + 10;
}

Now when we run gulp javascript we will get a notification window that an error was found.

1
2
3
4
5
6
7
$ gulp javascript

Using gulpfile ~/YOUR_DIRECTORY/gulpFile.js
Starting 'javascript'...
Validate JavaScript
gulp-notify: [Error running Gulp] Line 3: Unexpected token *
'javascript' errored after 41 ms Line 3: Unexpected token *

Gulp – Validate JavaScript

Gulp, Gulp – Validate JavaScript

The web runs on more than HTML and CSS, It runs on JavaScript. Let’s perform some common JavaScript build operations with gulp.

We will first look into validating our JavaScript using gulp.

And yes… there is a plugin for that.

1
$ npm install gulp-jsvalidate --save-dev

For more information on gulp-jsvalidate check out https://github.com/sindresorhus/gulp-jsvalidate.

We will now create a new Javascript task in our gulpfile.js. At first, all we will do is validate our Javascript in a new /contents/javascripts folder.

/gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
...
var jsValidate = require('gulp-jsvalidate');
...

...
gulp.task('javascript', function () {
  console.log("Validate JavaScript");
  return gulp.src("contents/javascripts/**.js")
    .pipe(jsValidate());
});
...

Time to test out our new task and plugin. Create a javascript file and make a syntax error.

/contents/javascript/somejs.js
1
2
3
4
function OMG() {
  var x * 2; // this is not valid!
  return x + 10;
}

Now when we run our javascript task, we will get an error message in the terminator:

1
2
3
4
5
$ gulp javascript

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'javascript'...
'javascript' errored after 14 ms Line 3: Unexpected token *

When we fix the error and then run our gulp task, we won’t get that error message:

/contents/javascript/somejs.js
1
2
3
4
function OMG() {
  var x = 2;
  return x + 10;
}
1
2
3
4
5
6
$ gulp javascript

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'javascript'...
Validate JavaScript
Finished 'javascript' after 14 ms

Sweet codes! Gulp can now check if our JavaScript is valid. But the error message in the console is rather bland, lets find a better way to tell us that we messed up.

Gulp – Handling Errors With Streams

Gulp, Gulp – Handling Errors with Streams

What if the file was named incorrectly? What happens?

Change the string index.html to OMG_WRONG_FILE.html and rerun the script.

/streams.js
1
2
3
4
5
6
7
8
9
10
11
var fs = require("fs");
var stream = fs.createReadStream(__dirname + "/contents/OMG_WRONG_FILE.html");

stream.on("data", function(chunk) {
  // just output chunk to terminal
  console.log(chunk.toString());
});

stream.on("end", function() {
  console.log("END");
});

Running the script this time results in:

1
2
3
4
5
6
$ node streams.js

events.js:72
        throw er; // Unhandled 'error' event

Error: ENOENT, open '~/YOUR_DIRECTORY/OMG_WRONG_FILE.html'

If we read the error message carefully, then we can see that there is an error event we can listen to. So lets listen for that event.

/streams.js
1
2
3
4
...
stream.on("error", function(er) {
  console.log("error", er);
});

Now we rerun the script and see:

1
2
3
4
5
6
$ node streams.js

error { [Error: ENOENT, open '/Users/bunlong/js/gulp/contents/OMG_WRONG_FILE.html']
  errno: -2,
  code: 'ENOENT',
  path: '~/YOUR_DIRECTORY/contents/OMG_WRONG_FILE.html' }

And that is it for now. We will come back and use some of what we learned later.

Gulp – Streams

Gulp, Gulp – Minify Our CSS

Before we continue, I think a brief detour to cover some basics of Node streams would be helpful.

Lets create a simple Node script to read a index.html file we will create:

/contents/index.html
1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
  <title>Learning Gulp</title>
</head>
<body>
  <h1>Hello Gulp!</h1>
</body>
</html>

First we require the file system library fs and create a read stream.

/streams.js
1
2
var fs = require("fs");
var stream = fs.createReadStream(__dirname + "/contents/index.html");

_dirname is a helper that returns the absolute path of the code file being run.

Node reads files asynchronously. This is normally where we could dive into what “non-blocking I/O” means vs threads, etc. This is a guide about gulp though so I will keep this detour basic.

For our purposes, this means that we have to listen to events from the stream to be able to read the file.

The events we are going to listen to are data and end.

data fires when a chunk of the file has been read and returned. This chunk is not always the entire file. In fact, you should assume it is not the entire file.

/streams.js
1
2
3
4
5
...
stream.on("data", function(chunk) {
  // just output chunk to terminal
  console.log(chunk.toString());
});

end fires when the file has been completly read.

/streams.js
1
2
3
4
5
...
stream.on("data", function(chunk) {
  // just output chunk to terminal
  console.log(chunk.toString());
});

Now altogether, streams.js looks like:

/streams.js
1
2
3
4
5
6
7
8
9
10
var fs = require("fs");
var stream = fs.createReadStream(__dirname + "/contents/index.html");

stream.on("data", function(chunk) {
  console.log(chunk.toString());
});

stream.on("end", function() {
  console.log("END");
});

Now if you run the node script in the terminal, you should see:

1
2
3
4
5
6
7
8
9
10
11
12
$ node streams.js

<!DOCTYPE html>
<html>
<head>
  <title>Learning Gulp</title>
</head>
<body>
  <h1>Hello Gulp!</h1>
</body>
</html>
END

So far so good, That’s it!!! See ya!!! :)

Gulp – Minify Our CSS

Gulp, Gulp – Minify Our CSS

Now since we have our css in a single file, we can continue to increase the performance of our site by minifying our css.

Minifying is the process of eliminating all the unnecessary formatting in a css file. Human’s need spaces and tabs (also known as white space) to more easily read the styles. A browser doesn’t care about white space so we can make the file smaller by removing them.

You should start seeing a pattern when using gulp… because for this we need to use a plugin:

1
$ npm install gulp-minify-css --save-dev

For more information on gulp-minify-css, check out https://www.npmjs.org/package/gulp-minify-css.

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var gulp = require('gulp');
var concat = require('gulp-concat');
var clean = require('gulp-rimraf');
var cssmin = require("gulp-minify-css");

gulp.task('clean', [], function() {
  console.log("Clean all files in build folder");

  return gulp.src("build/*", { read: false }).pipe(clean());
});

gulp.task('default', ['clean'], function() {
  console.log("Concat, move, and minify all the css files in styles folder");
  return gulp.src("contents/styles/**.css")
    .pipe(concat('main.min.css'))
    .pipe(cssmin())
    .pipe(gulp.dest('build/styles'));
});

Open up your terminator to run gulp:

1
2
3
4
5
6
7
8
9
$ gulp

Using gulpfile ~/YOUR_DIRECTORY/gulpFile.js
Starting 'clean'...
Clean all files in build folder
Finished 'clean' after 18 ms
Starting 'default'...
Concat, move, and minify all the css files in styles folder
Finished 'default' after 40 ms

Our build/styles/main.min.css should now look like:

1
p{font-size:30px}h1{color:red}

So far so good, That’s it!!! See ya!!! :)

Gulp – Cleaning Our Build Folder

Gulp, Gulp – Cleaning Our Build Folder

A normal part of a build process is a cleaning task to remove all the old files in the build folder.

For us, this means getting rid of the leftover more_styles.css and some_styles.css files in our build/styles folder.

To clean files, we will need another gulp plugin:

1
$ npm install gulp-rimraf --save-dev

For more information on gulp-rimraf check out https://www.npmjs.org/package/gulp-rimraf.

This task used to be handled by gulp-clean but has been replaced by gulp-rimraf.

Instead of adjusting our default task, lets create a new task to clean out the directory.

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var gulp = require('gulp');
var concat = require('gulp-concat');
var clean = require('gulp-rimraf');

gulp.task('clean', [], function() {
  console.log("Clean all files in build folder");
  return gulp.src("build/*", { read: false }).pipe(clean());
});

gulp.task('default', ['clean'], function() {
  console.log("Concating and moving all the css files in styles folder");
  return gulp.src("contents/styles/**.css")
    .pipe(concat('main.css'))
    .pipe(gulp.dest('build/styles'));
});

So like before, we need to require gulp-rimraf.

This time though, we created a new task called clean. We tell this task to look at all the files in the build folder and then pipe them to our clean operation. This will delete the files.

You might notice that in our options we pass in { read: false }. This tells the task to not read the contents of the files it is deleting. It is an easy performance gain.

To run our clean task from the command line, we just tell gulp which task to run:

1
2
3
4
5
6
$ gulp clean

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'clean'...
Clean all files in build folder
Finished 'clean' after 8.95 ms

What we would like is to run our clean task before we run our default task. That way our build folder will be nice and empty before we starting moving files there.

You might have been wondering what the empty array ([]) was before our function. This is where we specify dependency tasks.

A dependency task is a task that needs to be completed before gulp can run the current task.

So for our scenario, our clean task is a dependency for default.

gulpfile.js
1
2
3
4
...
gulp.task('default', ['clean'], function() {
  ...
});

Now when we run our default gulp task, we should see that it runs the clean task before default.

1
2
3
4
5
6
7
8
9
$ gulp

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'clean'...
Clean all files in build folder
Finished 'clean' after 9.03 ms
Starting 'default'...
Concating and moving all files from styles folder
Finished 'default' after 8.42 ms

So far so good, That’s it!!! See ya!!! :)

Gulp – Concat: Combining Multiple Files Into One

Gulp, Gulp – Concat: Combining multiple files into one

Printing Hello and moving files is rather boring. Let’s do something productive.

When we create websites, we are always trying to deliver the best experience possible. This includes having our webpages displaying fast. Back in the day, this meant having all our styles in one css file.

While this made our webpages load faster, it made maintaining the css file a night-mare!

These days we can use multiple css files for better organization and then concat (meaning merge or combine) the files together into one large file.

We left our project looking like:

1
2
3
4
5
6
7
8
9
10
11
/build
  /styles
    some_styles.css
    more_styles.css
/node_modules
/contents
  /styles
    some_styles.css
    more_styles.css
gulpfile.js
package.json

Right now, we have two separate css files in our build/styles folder. We are going to use a gulp plugin to concat all our css files in the styles folder.

Gulp contains some basic tasks, but the power of gulp is the customization you can bring into your build process by using plugins.

For a list of all the gulp plugins available, go to http://gulpjs.com/plugins/

To concat the files together, we will need to install one of these plugins.

1
npm install gulp-concat --save-dev

We can then update our default gulp task to concat the files.

gulpfile.js
1
2
3
4
5
6
7
8
9
var gulp = require('gulp');
var concat = require('gulp-concat');

gulp.task('default', [], function() {
  console.log("Concating and moving all the css files in styles folder");
  gulp.src("contents/styles/**.css")
    .pipe(concat('main.css'))
    .pipe(gulp.dest('build/styles'));
});

Couple of things have changed, can you spot them? First, we had to reference the gulp plugin with:

1
var concat = require('gulp-concat');

We chose to label this concat. Obviously we could call it anything we want, but concat communicates what the plugin does to those reading our build script.

Second, we added another step to our task. In between the src and the pipe(gulp.dest...) steps, we added pipe(concat(...)).

Gulp works by streaming files from one process to another. This allows us to create complex build tasks out of small, simple steps. Composition == winning.

Now run our gulp task:

1
2
3
4
5
$ gulp
Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'default'...
Moving all the css files in styles folder
Finished 'default' after 6.09 ms

Our task will read all the css files in the styles folder, combine them into one main.css file, and then place that file in the build/styles folder.

Our project should now look like:

1
2
3
4
5
6
7
8
9
10
11
/build
  /styles
    main.css
    more_styles.css
    some_styles.css
/node_modules
/styles
  more_styles.css
  some_styles.css
gulpfile.js
package.json

Notice the more_styles.css and some_styles.css files are still in our build folder. :(

We don’t want those chumps there anymore. In the next chapter we will learn how to get rid of those files.

So far so good, That’s it!!! See ya!!! :)