Gulp – Web Server

Gulp, Gulp – Web Server

We can actually serve our webpages by using a gulp plugin.

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

For more information about gulp-webserver check out https://www.npmjs.com/package/gulp-webserver

gulpfile.js
1
2
3
4
5
6
7
...
var webserver = require('gulp-webserver');
...
gulp.task('webserver', function() {
  return gulp.src('build')
    .pipe(webserver());
});

Now when we run our gulp task webserver we will have a local webserver to view our website.

1
2
3
4
5
6
$ gulp webserver

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'webserver'...
Webserver started at http://localhost:8000
Finished 'webserver' after 20 ms

If you go to http://localhost:8000 in your web browser you should see our index.html page saying Hello Gulp!.

Gulp – Watch

Gulp, Gulp – Watch

Now for something super amazing. Instead of running the gulp task explicitly, lets have gulp run our tasks when the files change.

First reorganize some of our tasks:
- Rename default task to css.
- Create a new default task to run css, ‘javascript’, and ‘homepage’ tasks.

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
...
gulp.task('css', ['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'));
});
...
gulp.task('default', ['css', 'homepage', 'javascript']);
...

Next create our file watching task. Could you guess what?… there isn’t a plugin for this. It is just part of gulp.

We will create a gulp watch task to watch our contents folder and run our default task on file change.

gulpfile.js
1
2
3
4
5
...
gulp.task('watch', [], function() {
  return gulp.watch(['contents/**'], ['default']);
});
...

In the terminal type:

1
2
3
4
5
$ gulp watch

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'watch'...
Finished 'watch' after 11 ms

If you update any of the css files in the styles folder, you should see gulp run the default task.

1
2
3
4
5
6
7
8
9
10
11
12
13
Starting 'clean'...
Clean all files in build folder
Finished 'clean' after 21 ms
Starting 'css'...
Concat, move, and minify all the css files in styles folder
Starting 'homepage'...
Starting 'javascript'...
Validate, Concat, Uglify, and Move all the javascript files
Finished 'homepage' after 77 ms
Finished 'javascript' after 75 ms
Finished 'css' after 84 ms
Starting 'default'...
Finished 'default' after 14 μs

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!!! :)