Skip to content

Overview of workflow automation [build-tool, gulp, webpack, grunt, js, node]

License

Notifications You must be signed in to change notification settings

ga-wdi-lessons/build-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Tools

Learning Objectives

  • Define build tools
  • Introduce available build tools
  • Compare and contrast Gulp to Rake
  • Use build tools to automate workflow

Framing (5 min)

Build Tools are software that allows us to speed up and automate our work, including compiling our code, minification, concatenation, server automatic browser reloading, etc..

Q. What is minified code and why do we do it use it?
Code in which all unnecessary characters have been removed from source code without changing its functionality at all. It allows for a faster response and significantly reduces the size of the code, requiring less bandwidth.


Q. When have you seen minified code before?
CDNs, jQuery, Angular



Q. What is complied code and what is used for?

Taking preprocessors like SASS, CoffeeScript, HAML, etc and converting it into css/html/javascript so our browser knows how to read it!



Q. What tool did we use in Rails to help concatenate and minify our JS and CSS?
Rails Asset Pipeline, which helped to concatenate all JS and CSS files into one, allowing us to speed up our application and serving of static files.



In summary, build tools help us to compile our code so we don't have to do it manually and repeatedly.

Gulp (5 min)

Gulp

Gulp is an popular open-source automation tool built on Node.js that runs tasks to manipulate files in your application.

It is commonly used for bundling, minificiation, ES6 support, etc..

Gulp Documentation

It's really similar to rake in Ruby!

Rake is used to help automate a bunch of tasks for us when building Rails applications.

For example, when running the following:

$ rake db:migrate

How would we accomplish this without using rake ?

You-Do: Gulp Linter Example (15 min)

Fork and Clone this Repo: https://github.com/ga-wdi-exercises/gulp-bamsay

Solution Code

Gulp Installation

$ npm install gulp -g

install globally

$ npm install gulp --save-dev

install as dependency in project

Note we writing --save-dev instead of --save. This adds a separate "devDependencies" object in which we will be saving our dependences. Don't worry too much about the difference for now, just know these plugins would be used differently in production!

$ touch `gulpfile.js`

This needs to be installed in your root directory and will contain all of your related task configuration

Now let's run gulp in our terminal:

$ gulp
[10:36:31] Using gulpfile ~/wdi8/lessons/build-tools/gulp-example/gulpfile.js
[10:36:32] Task 'default' is not in your gulpfile
[10:36:32] Please check the documentation for proper gulpfile formatting

Uh oh! - we ran into a problem! We need to define a gulp task

Gulp Tasks

Q. What is a task?

In Gulp, we create tasks that can transform our code. `gulp.task` is a method which we use to define our tasks. Its arguments are the task name, it's dependencies and callback function.




In our gulpfile.js we need to include the gulp module. To do this, we should define a variable: var gulp = require('gulp'); This will allow us to call upon Gulp to create a task.

var gulp = require('gulp');

My First (Default) Task

By default, Gulp requires a default task. It is the first task that Gulp will look for when reading your gulpfile.js.

Let's define our first (default) task below in our gulpfile.js:

var gulp = require('gulp');

//define a task with the name of 'default'
// and a callback to perform when the task is ran
gulp.task('default', function() {
  console.log('I am the default task!');
});

In your terminal, run $ gulp.

This will have the library look for a default task in your gulpfile.js. It will then execute the callback that you define for your task.

You should see something like the following:

[10:37:37] Using gulpfile ~/wdi8/lessons/build-tools/gulp-example/gulpfile.js
[10:37:37] Starting 'default'...
I am the default task!
[10:37:37] Finished 'default' after 104 μs
Q. Does the string `'default'` matter what we call it?
Yes, and no. While you can certainly change `default` to `wombat`, it would not be very descriptive of the task.

Also, whatever you define 'name' as in your task: gulp.task('name', callbackFunction), you also need to run the following in the command line:

$ gulp <name>

If our task's name is `default`, we can just run `$ gulp`


STOP

Review (5 min)

Let's Review and Answer questions!

You-Do: Write your own gulp task (5 min)

Let's get more practice writing gulp tasks. Let's write a task that will console.log today's date once's it run.

See Solution

Plugins (5 min)

Gulp plugins

Gulp has many additional plugins that we can use in our applications. We need to install them each individually using npm install <dependency-name> --save-dev

Break (10 min)

You-Do: Jshint Plugin Example (15 min)

We are going to be working with the jshint and jshint-stylish plugins so we can easily identify any javascript errors in our code!

$ npm install jshint gulp-jshint --save-dev
$ npm install jshint-stylish --save-dev

In our gulpfile.js, let's add the following modules for jshint:

var gulp   = require('gulp');
var jshint = require('gulp-jshint');
var stylish = require('jshint-stylish');

Nothing new here, just requiring our dependencies we will be using

Now, let's add our task!

gulp.task('jshint', function() {
  return gulp.src('./js/*.js')
  .pipe(jshint())
  .pipe(jshint.reporter(stylish));
});
Q. What is `gulp.src`?

gulp.src is specifying our file source paths. It can also take an array of source paths.




Q. What do you think `'./js/*.js'` represents ?
The * is looking for any file ending in `.js` in the specified directory.

Essentially, it's linting all of of javascript files in our `js` directory



Q. What is `.pipe()` in gulp?
.pipe() is used to pipe the source file/files into a plugin. These pipes can chain tasks together so you can add as many dependencies as you need!



--------------

Now, run the following in your command line:

$ gulp jshint

Note, we can also add this as our default task. Then, we would simply run $ gulp instead.

STOP

Review (5 min)

Let's Review and Answer questions!

We-do: Gulp SASS (25 min)

Gulp-Sass

Let's use Gulp to compile our sass into css!

We are going to be using the following gulp-sass plugin.

Gulp-Sass.

Feel free to continue along on the same gulpfile.js. However, always feel free to checkout to jshint-solution as well for working code.

First, let's install and save our plugin:

$ npm install gulp-sass --save-dev

Next, let's require the gulp-sass dependency:

var gulp = require('gulp');
var sass = require("gulp-sass");

And then let's add a new task:

gulp.task('sass', function () {
 return gulp.src('./css/**/*.scss')
   .pipe(sass().on('error', sass.logError))
   .pipe(gulp.dest('./css/'))
});
Q. What do we see here in this task thats different?
1. sass.logError, which will display an error

2. gulp.dest()

* `gulp.dest` will copy our results to a given directory. In this case to our `css` directory.



$ gulp sass

Great, it worked! But how is that helpful to our workflow?

While Gulp does a great job of executing these types of tasks well, it becomes extremely powerful when we start compose these different tasks together.

Gulp Watch & Connect

We are going to add these two dependencies as well! Our goal is for us to see any reflected sass changes without manually reloading the server or refreshing our browser.

Finally, we will be integrating all the tasks to help automate our workflow at the end.

Gulp Watch

Gulp Connect

$ npm install gulp-watch --save-dev

$ npm install gulp-connect --save-dev
var gulp = require('gulp');
var sass = require("gulp-sass");
var watch = require('gulp-watch');
var connect = require('gulp-connect');
gulp.task('watch', function () {
 gulp.watch('./css/**/*.scss', ['sass']);
});
gulp.task('connect', function() {
  connect.server({
    livereload: true
  })
});
Q. What `gulp.watch()` doing ?

It's a method that checks to see if a file was saved.  It's "watching" our sass file for changes.



Q. What is `connect.server({livereload: true})` doing?

Reloading and Refreshing our browser without us have to do so manually. It's going to start up a web server that will allows us to reload our complied files and refresh our browser automatically.




Let's now add .pipe(connect.reload()) to our sass task:

gulp.task('sass', function () {
 return gulp.src('./css/**/*.scss')
   .pipe(sass().on('error', sass.logError))
   .pipe(gulp.dest('./css/'))
   .pipe(connect.reload());
});

Finally, let's add the following at the bottom of our gulpfile.js:

gulp.task('default', ['sass', 'connect', 'watch']);

$ gulp
Q. Why do we not need to specify our task in the above command?
Because we used `default`, gulp will automatically execute the `default` command


[13:25:59] Using gulpfile ~/wdi8/sandbox/bamsay/gulpfile.js
[13:25:59] Starting 'sass'...
[13:25:59] Starting 'connect'...
[13:25:59] Finished 'connect' after 18 ms
[13:25:59] Starting 'watch'...
[13:25:59] Finished 'watch' after 15 ms
[13:25:59] Server started http://localhost:8080
[13:25:59] LiveReload started on port 35729
[13:25:59] Finished 'sass' after 72 ms
[13:25:59] Starting 'default'...
[13:25:59] Finished 'default' after 3.63 μs

Break (10 min)

Webpack (10 mins)

Webpack Documentation

webpack is known as a "code bundler". It is used to bundle JavaScript files to run in our browsers, and can be used for transforming, bundling, or packaging assets and resources.

In essence, it takes your code, transforms and bundles it, then returns a brand new version of your code.

We will be using Webpack with React! You will see that React uses something called JSX, which leads to a fairly large cost in size. Webpack alleviates this somewhat by compiling and bundling the code together.

While Gulp is known as a "task runner", webpack does a little more. Task runners will compile your code as shown in the previous section. Webpack similarly can compile code, but takes things a step further by bundling modules and files together. What does that mean exactly?

In short, it allows for faster development. Task runners like Gulp and Grunt need to rebuild the entire application every time you save. Bundlers like webpack only rebuild the modules you have specifically edited!

You-do: Setup

git checkout webpack_starter

run: npm install -g webpack run: npm install webpack --save-dev

Try testing it out by running webpack in the terminal

You should see something like:

webpack 1.12.12
Usage: https://webpack.github.io/docs/cli.html

Options:
  --help, -h, -?
  --config
  --context
  --entry
...
  --display-cached-assets
  --display-reasons, --verbose, -v

Output filename not configured.
What does this tell us?
We haven't configured what we actually want to do with webpack yet!


STOP (5 mins)

You Do: Configuring the Webpack (10 mins)

To actually configure our webpack, we need to create a new file in the root of our directory: webpack.config.js

Here we will need to define the folders and files that we want bundled, as well as any additional functionality from our tool. We are going to add in a few pieces of code:

var path = require('path');

var PATHS = {
  js: path.join(__dirname, 'js'),
  build: path.join(__dirname, 'build')
};
What do you think this first part is doing?
This simply defines two folders within our app that we will be either reading or modifying with webpack.

*Note* 'path' is a node method


module.exports = {
  // Entry accepts a path or an object of entries. We'll be using the
  // latter form given it's convenient with more complex configurations.
  entry: {
    js: PATHS.js
  }
What do you think this next section does?
Here we are defining the entry point of our webpack. In other words, whatdirectory do we want to look into and bundle?


Lastly, we will need to define the output. Where are we going to put thebundled code?

,
  output: {
    path: PATHS.build,
    filename: 'bundle.js'
  }
};

Try running webpack in your terminal again. What happens?

Check out your public folder and see what file was added in!

STOP (10 mins)

Having to run webpack every time you make a change will get frustrating (and boring) quickly. We can set up the webpack-dev-server to help us out!

One of the best features of the dev server is Hot Module Replacement (HMR). This is a feature provided by webpack that will update specific modules live. In other words, we can potentially update specific parts of our app without having the refresh the entire page!

You Do: Setting up Webpack Server (10 mins)

To get started, run the following in you command line:

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

We can now add this directly to our package.json file, allowing us the ability to start up our application immediately on the webpack dev server. Include the following in your scripts object in the package.json file:

"start": "webpack-dev-server --content-base build"

Go ahead and run npm start in your terminal. You should see something like:

npm Start

If you open your browser to localhost:8080, you should see your index.html and any html rendered through your index.js file on the browser!

This is cool and all, but not really any different than before other than running a different command in your terminal. We want to be able to make changes to our code and see the updates without having to do any additional refreshing.

We will have to install another package to get this up and running: npm i webpack-merge --save-dev

This package allows us to merge objects together. In this case, we will be merging the object that sets our paths, with HMR.

In webpack.config.js, include the following on line 2:

var merge = require('webpack-merge');
var webpack = require('webpack');
var TARGET = process.env.npm_lifecycle_event;

We will then replace everything from module.exports and down with the following:

var common = {

  // Entry accepts a path or an object of entries. We'll be using the
  // latter form given it's convenient with more complex configurations.
  entry: {
    app: PATHS.js
  },
  output: {
    path: PATHS.build,
    filename: 'bundle.js'
  }
};

// Default configuration
if(TARGET === 'start' || !TARGET) {
  module.exports = merge(common, {});
}

if(TARGET === 'build') {
  module.exports = merge(common, {});
}
What is this doing??
Depending on the command we run (start, or build) we will be merging the 'common' object with whatever we include in the empty objects


Now all we are missing is adding in the HMR. Let's edit the first if statement to include this:

if(TARGET === 'start' || !TARGET) {
  module.exports = merge(common, {
    devServer: {
      contentBase: PATHS.build,
      // Enable history API fallback so HTML5 History API based
      // routing works. This is a good default that will come
      // in handy in more complicated setups.
      historyApiFallback: true,
      hot: true,
      inline: true,
      progress: true,
      // Display only errors to reduce the amount of output.
      stats: 'errors-only',
      // Parse host and port from env so this is easy to customize.
      // If you use Vagrant or Cloud9, set
      // host: process.env.HOST || '0.0.0.0';
      // 0.0.0.0 is available to all network devices unlike default
      // localhost
      host: process.env.HOST,
      port: process.env.PORT
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin()
    ]
  });
}

And remove --content-base build from your package.json

COOL. Everytime we make a change to the js file and save the browser is automatically updated!

You Do: Add in CSS watch! (10 mins)

We already have the setup done, so adding in CSS watch is simple!

We'll need another package for this:

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

We then need to add the use of this package in as a module within our common variable after output:

,
module: {
  loaders: [
    {
      // Test expects a RegExp! Note the slashes!
      test: /\.css$/,
      loaders: ['style', 'css'],
      // Include accepts either a path or an array of paths.
      include: PATHS.css
    }
  ]
}

If you run it now, an error about "Promise" will pop up. This is an ES6 bug that can be fixed with this:

npm install es6-promise

var Promise = require('es6-promise').Promise;

Something still seems off.. Notice the include:. That is pointing to a PATHS.css, but we haven't created that yet! Let's go ahead and add that in.

Try to think about what to do here before looking at the answer!
Inside your PATHS object, add `css: path.join(__dirname, 'css')`. Then, within your `common.entry` include `css: PATHS.css`


Go to your style.css and change the background to green, what happens?? Notice how the page does not refresh!

Bonus: Figure out SASS configuration

Closing (5 min)

Example of Grunt in the "wild"

Quiz Questions:

  1. What type of tasks can build tools help us to automate?
  2. What is Gulp and what environment is it used for?
  3. Where do we save all of our Gulp dependencies?
  4. What is the difference between gulp.src() and gulp.dest()?
  5. What does the gulp connect plugin allows us to do?
  6. How do Gulp and Webpack differ?
  7. Why does webpack work well with React?

Additional Resources

Webpack as middleware

Webpack compared

webpack tutorials

Gulp Tutorial

Grunt Tutorial

About

Overview of workflow automation [build-tool, gulp, webpack, grunt, js, node]

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •