Integrate tooling

Goals:
------

- Bootstrap this project with Node and NPM.
- Install Grunt locally and Grunt-CLI globally.
- Ignore `node_modules` dependencies.
- Add in configurations for JSHint and JSCS.
- Put initial application boilerplate in place.

Instructions:
-------------

- Copy in the `src` directory from `_solution` to test your work.
  - This contains the initial application source that we'll dive into later.

- Use the `npm init` command to create a `package.json`.
  - Accept all defaults if you are unsure.
  - Add `private: true` to ensure this project will not be accidentally
    published.

- Install the `grunt-cli` globally, with:

  npm install grunt-cli -g

  You may need to use `sudo` if you are on Linux or OS X.

- Install and save the following dependencies locally:
  - grunt
  - grunt-jscs
  - grunt-contrib-jshint
  - grunt-contrib-watch
  - grunt-contrib-connect

- Create a `.gitignore` file that contains `/node_modules/`.

- Create a JSCS file `.jscsrc` with some configuration options:

  {
    "validateIndentation": 2,
    "requireSpaceAfterKeywords": [
      "if",
      "else",
      "for",
      "while",
      "do",
      "switch",
      "return",
      "try",
      "catch"
    ],
    "maximumLineLength": 80,
    "validateQuoteMarks": "'",
    "disallowTrailingWhitespace": true
  }

- Create a JSHint file `.jshintrc` with some configuration options;

  {
    "curly": true,
    "eqeqeq": true,
    "immed": true,
    "latedef": "nofunc",
    "newcap": true,
    "noarg": true,
    "sub": true,
    "strict": true,
    "undef": true,
    "unused": "vars",
    "boss": true,
    "eqnull": true,
    "node": true
  }

- Create a `Gruntfile.js` in the project root.

- Add in the Grunt configuration ceremony:

  module.exports = function(grunt) {
    grunt.initConfig({

    });
  };

- Load all the tasks using `grunt.loadNpmTasks`.

  Example:

  grunt.loadNpmTasks('grunt-contrib-connect')

- Configure `connect` with the following options:

  dev: {
    options: {
      base: ['src'],
      port: '8000',
      hostname: '*',
    },
  },

- Configure `jscs`:

  options: {
    config: '.jscsrc',
  },
  all: [
    '<%= jshint.build.src %>',
    '<%= jshint.app.src %>',
  ],

- Configure `jshint`:

  build: {
    options: {
      jshintrc: '.jshintrc',
    },
    src: '*.js',
  },
  app: {
    options: {
      jshintrc: 'src/.jshintrc',
    },
    src: 'src/**/*.js',
  },

- Configure `watch`:

  livereload: {
    options: {
      livereload: true,
    },
    files: ['src/**/*.{js,css,html}'],
  },
  jshintrc: {
    files: ['**/.jshintrc'],
    tasks: ['jshint'],
  },
  build: {
    files: ['<%= jshint.build.src %>'],
    tasks: ['jscs', 'jshint:build'],
  },
  scripts: {
    files: ['<%= jshint.app.src %>'],
    tasks: ['jscs', 'jshint:app'],
  },

- Create a `lint` task using `grunt.registerTask` to alias jscs and
  jshint.
- Create a `dev` task to alias `lint`, `connect:dev`, and `watch`.
- Add an NPM script `test` to run `grunt lint`.
Move Grunt task configuration into build folder

Goals:
------

- Break out the Grunt configuration from a single file to several small
  files.
- Load all the individual configurations into the Gruntfile.

Instructions:
-------------

- Create a `build` folder.

- Create files matching each task in the `build` folder, can be named
  anything so long as it ends in .js.
    - We recommend naming them after the plugin names.

- Remove all redundant configuration from `grunt.initConfig`.

- Load the tasks from the main Gruntfile.
  - Make sure you put `grunt.loadTasks` after `grunt.initConfig`
    otherwise the configuration will not be ready.
Add sample REST server and "server" task

Goals:
------

- Integrate an API server that will run alongside the static connect
  server.
- Illustrate how to run two servers simulataneously.

Instructions:
-------------

- Copy the *_solution/server* directory into the work directory.
  - This contains the API code and a sample sqlite3 database.

- Save the sqlite3 module with NPM.
  - Use a command like `npm install sqlite --save-dev` to install and
    save in the package.json file.

- Add a server task with the following code

  grunt.registerTask('server',
    'Start the REST and connect servers.',
    function() {
      require('./server/server');
      grunt.task.run('connect:' + this.args.join(':'));
    }
  );

  - This will load the API server and the connect task simultaneously.

- Add this task to the dev alias.
Add bower.json and dependencies

Goals:
------

- Integrate client-side package management.
- Ignore new dependency folders.
- Add the correct dependencies to Bower.

Instructions:
-------------

- Globally install bower with:

  npm install -g bower

  - You may need to use `sudo` with Linux or OS X.

- Create a `bower.json` file with `bower init`.
  - Accept the defaults with enter.

- Install the following dependencies with `bower install --save`:
  - jquery
  - lodash
  - backbone

- Utilizing the NPM scripts, add an install hook to `bower install`.

- Add `/bower_components/` to `.gitignore`.

Tips:
-----

- You will need Git in your PATH before Bower will work.
Adding in the photo component

Goals:
------

- Becoming familiar with Backbone structure.
- Organizing our code into "modules".
- Including the source.

Instructions:
-------------

- Update `build/grunt-contrib-connect.js` to have the root folder as a base.
  - This is an array, so simply add a new item containing `.` to it.

- Update the `src/.jshintrc` file to support the following globals:
  - Backbone
  - $
  - _
  - Photo
  - PhotoView

- Create an invalid (hidden) script tag in `src/index.html` with the
  following traits:

  - type="template/text"
  - id="photo-detail"

- And the following source:

   <% if (typeof dataUri !== "undefined") { %>
     <div class="photo">
       <img src="<%= dataUri %>"/>
     </div>
   <% } else { %>
     <div class="error">Photo not found!</div>
   <% } %>

- Add script tags pointing to jQuery, Lo-Dash, and Backbone from the
  `bower_components` folder into `src/index.html`.
  - Make sure they are in the right order.

- Add the modules script tags into `src/index.html` before `/main.js`.
  - `/modules/photo.js`
  - `/modules/view.js`

- Replace `src/main.js` contents with:

  var me = new Photo({
    id : 19
  });

  var view = new PhotoView({
    model : me
  });

  view.$el.appendTo('body');

  me.fetch({
    success: function() {
      view.render();
    }
  });

- Copy in the `_solution/src/modules` folder to the `src` directory.
  - We'll discuss the contents.
Add in Vagrant configuration

Goals:
------

- Add a Vagrantfile that describes the environment.
- Ignore the generated `.vagrant` in source control.
- Create a provision script that installs all dependencies necessary to
  run the Roostagram application.

Instructions:
-------------

- Add `/.vagrant` to your `.gitignore` file.
  - Don't want to accidentally commit this folder.

- Initialize the ubuntu/trusty64 box with the local or remote URL.

- Create the `provision.sh` file.

- Associate the above file in the Vagrantfile created by `vagrant init`.

  # Using the shell provisioning script, with user privs by default.
  config.vm.provision "shell", privileged: false, path: "provision.sh"

- Fill out provision.sh:
  - Apt packages:
    - build-essential
    - git
    - nodejs-legacy
    - npm
  - NPM global packages:
    - grunt-cli
    - bower
  - NPM local install.

- Forward the following ports:
  - 8000
  - 8001
  - 35729

Tips:
-----

- Verify your VM has Internet connectivity by:

  vagrant ssh
  ping google.com

- Experiment in the VM first and then add to the provision script.
- Update aptitude with apt-get update.
- Bower requires setting `export CI=true` before using.
- Redirect stdout with >/dev/null and stderr to stdout with 2>&1
Incorporate RequireJS

Goals:
------

- Install and configure RequireJS runtime.
- Use CommonJS-style AMD.

Instructions:
-------------

- Using Bower, install and save:
  - requirejs

- Inside `src/.jshintrc` remove old globals and replace with:

  "globals": {
    "require": true,
    "define": true
  }

- Replace all application-related script tags in `src/index.html` with:

  <script src="/bower_components/requirejs/require.js"
  data-main="/requirejs-config"></script>

- Convert the `src/modules` to Simplified CommonJS.

  define(function(require, exports, module) {
    'use strict';

    /* ... */
  });

- Add in a `src/requirejs-config.js` file to contain the runtime and
  optimizer configuration.

  require.config({
    baseUrl: '/',

    paths: {
      backbone: 'bower_components/backbone/backbone',
      jquery: 'bower_components/jquery/dist/jquery',
      underscore: 'bower_components/lodash/dist/lodash'
    },

    deps: ['main']
  });

  - I'll explain the contents after the exercise.
Build and reorganize modules folder to components

Goals:
------

- Refactor the application layout to be more structured for components

Instructions:
-------------

- Create `components` folder inside `src/modules`

- Create 'src/modules/photo' component folder

- Move `src/modules/photo.js` and `src/modules/view.js` into this new
  component folder

- Rename `photo.js` to `model.js`

- Update `src/main.js` to reflect new paths to `model.js` and `view.js`
Refactor view to use an external html template

Goals:
------

- Utilize our runtime to make working with templates much easier

Instructions:
-------------

- Using Bower, install and save:
  - lodash-template-loader

- Move the template contents from:
  `src/index.html` into -> `src/modules/components/photo/detail.html`.

- Delete the now empty script tag from `src/index.html`.

- Update the `requirejs-config.js` configuration to add a reference for
  the template plugin:

  paths: {
    tmpl: 'bower_components/lodash-template-loader/loader'
  }

- Since the plugin is expecting the module identifier `lodash` we need
  to map it using the standard AMD map configuration to `underscore`,
  which is actually Lo-Dash in disguise:

  map: {
    tmpl: {
      lodash: 'underscore'
    }
  }

- Rename view.js to detail.js and update main.js to look for new name

- Update detail.js to require and use the template:

  var template = require('tmpl!./detail')
Build Stylus into CSS

Goals:
------

- Leverage a CSS preprocessor to get access to variables and a more
  streamlined build.
- Create and maintain a `prod` folder which will hold all generated
  build files.

Instructions:
-------------

- Since you won't be writing CSS, the stylus files are provided for you in the
  `_solution/src` directory.  Simply copy this folder over to your work
  directory.

- Using NPM, install and save the following dependencies:
  - grunt-contrib-clean
  - grunt-contrib-stylus

- Configure the clean task, by creating a new file in the build
  directory with the following configuration:

  prod: 'prod',

  - This will remove the prod directory after every rebuild, giving us
    more consistency.

- Configure the stylus task with the following options:

  dev: {
    options: {
      compress: false,
      import: ['nib', 'shared'],
      paths: ['src/styles'],
    },
    src: [
      'src/styles/app.styl',
      'src/modules/**/*.styl',
    ],
    dest: 'prod/app.css',
  },

- Configure the watch task to account for the new stylus files:

  - First update livereload to account for the prod files (and removing
    the css extension):

    livereload: {
      options: {
        livereload: true,
      },
      files: ['src/**/*.{js,html}', 'prod/*'],
    },

  - Then add a section for stylus files:

    stylus: {
      files: ['src/**/*.styl'],
      tasks: ['stylus:dev'],
    },

- Configure the connect task to search through the new `prod` folder as
  a base as well (to find the new styles).

- Ensure that `index.html` has this line as the only stylesheet:

    <link rel="stylesheet" href="/prod/app.css">

- If everything works right you should be able to start `grunt`, modify
  Stylus files, and get relatively immediate feedback in the browser.

- If the above doesn't work it's probably because the watch task isn't picking
  up the css changes, so try messing with the file extensions to make it work.
Compile index page with jade

Goals:
======

- HTML doesn't have logic, but Jade does, we can conditionally change when to
  use LiveReload and when to use production assets.

- This gives us more flexibility.

Instructions:
=============

- Install and save `grunt-contrib-jade` with NPM.

- Add a `grunt-contrib-jade.js` file in the `build` folder with the
  same boilerplate.

- Add in the following configuration:

  dev: {
    src: 'src/index.jade',
    dest: 'prod/index.html',
  },

- Rename `src/index.html` to `src/index.jade`.

- Convert the contents to:

  doctype html
  html
    head
      meta(charset='utf-8')
      meta(http-equiv='X-UA-Compatible', content='IE=edge')
      title Roostagram!
      link(rel='stylesheet', href='/app.css')
    body
      #app
      script(src='/bower_components/requirejs/require.js', data-main='/requirejs-config')
      script.
        document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')

  - This above syntax is whitespace sensitive

- Add `jade:dev` to the `setup-dev` task anywhere after `clean:prod`, since
  we'll be adding this file to the generated directory.

- We'll also want the watch task to automatically pick up changes to the Jad e
  file, so update the watch task to have the following configuration:

  jade: {
    files: ['<%= jade.dev.src %>'],
    tasks: ['jade:dev'],
  },
Create preliminary "prod" build process

Goals:
======

- Prepare for future commits where optimized JS, CSS, HTML will be
  created, ready for deployment.
- Bring awareness of the build environment to Jade.
- Integrate R.js to build a single JS file.

Instructions:
=============

- Using NPM, install and save:
  - grunt-contrib-requirejs
  - grunt-contrib-copy

- Create `build/grunt-contrib-requirejs.js` with the following
  configuration:

  prod: {
    options: {
      baseUrl: '.',
      mainConfigFile: 'src/requirejs-config.js',
      insertRequire: ['main'],
      name: 'bower_components/almond/almond',
      out: 'prod/app.js',
      optimize: 'uglify2',
      generateSourceMaps: true,
      preserveLicenseComments: false,
      paths: {
        main: 'src/main',
        modules: 'src/modules',
      },
    },
  },

- Install and save `almond` with Bower.

- Update the stylus build task to have a prod target; add this object
  after the dev target:

  prod: {
    src: '<%= stylus.dev.src %>',
    dest: '<%= stylus.dev.dest %>',
  },

- Update the Grunt connect task to point to the production folder, so we can see the assets:

  prod: {
    options: {
      base: ['prod'],
      keepalive: true,
    },
  }

- Update Jade to know what environment we're in by update the `build/grunt-contrib-jade.js` task with:

  options: {
    data: {
      target: '<%= grunt.task.current.target %>',
    },
  },
  dev: {
    src: 'src/index.jade',
    dest: 'prod/index.html',
  },
  prod: '<%= jade.dev %>',

- Now we can utilize some logic in the Jade template to include the correct
  build assets, change the body section to look like:

  body
    #app
    if target == 'dev'
      script(src='/bower_components/requirejs/require.js', data-main='/requirejs-config')
      script.
        document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')
    else
      script(src='/app.js')

- Compile and optimize stylus for prod

- Ensure the `src/requirejs-config.js` file looks something like:

  require.config({
    baseUrl: '/',
    paths: {
      backbone: 'bower_components/backbone/backbone',
      jquery: 'bower_components/jquery/dist/jquery',
      underscore: 'bower_components/lodash/dist/lodash.underscore',
      lodash: 'bower_components/lodash/dist/lodash',
      tmpl: 'bower_components/lodash-template-loader/loader',
      vintagejs: 'bower_components/vintagejs/dist/jquery.vintage',
    },

    deps: ['main']
  });

- Finally, add a `prod` task in the Grunt configuration to run the build:

  grunt.registerTask('prod',
    'Compile for production.',
    [
      'test-unit', 'clean:prod', 'jade:prod', 'stylus:prod', 'requirejs',
      'server:prod'
    ]);

- Cross your fingers and try running `grunt prod`.

- You have broken images, this is okay!
Incorporate Browserify

Goals:
------

- Reuse existing source format (AMD) and package manager (Bower) with a
  different optimizer.

Instructions:
-------------

- Using NPM, install and save:

  - deamdify
  - debowerify
  - jstify
  - grunt-browserify
  - underscore (for jstify)

- Delete `build/grunt-contrib-requirejs.js` and
  `src/requirejs-config.js`

- Remove lodash-template-loader, almond, and requirejs from
  `bower.json`.

- Configure browserify with:

  options: {
    transform: ['jstify', 'deamdify', 'debowerify'],
    browserifyOptions: {
      debug: true
    }
  },

  prod: {
    files: {
      'prod/app.js': ['src/main.js']
    }
  }

- Add in Connect middleware to automatically rebuild the bundle on
  refresh:

  middleware: function(connect, options, middlewares) {
    var bundle = browserify(__dirname + '/../src/main.js', {
      transform: ['jstify', 'deamdify', 'debowerify'],
      external: ['lodash', 'jquery'],
      debug: true
    });

    return [['/prod/app.js', bundle]].concat(middlewares);
  }

- (Optional) Add browserify task to the watch configuration for scripts
  so that it's automatically rebuilt on file changes.

- Update the `src/index.jade` file to load livereload in dev, and always
  load `/prod/app.js`.

- Since we're no longer using lodash-template-loader, and instead using
  jstify, update the photo/detail module to load the template:

  var template = require('./detail.html');
Convert to CJS

Goals:
------

- Simple, more compact syntax for modules.

Instructions:
-------------

- Since we're unwrapping the modules, you will need to change the
  `src/.jshintrc` file to `node: true`.
  - Otherwise it will complain about function form of `use strict`.

- Unwrap `src/main.js`, `src/modules/components/photo/detail.js`, and
  `src/modules/components/photo/model.js` as CJS modules instead of AMD.

- Remove `deamdify` from `package.json`, the browserify task
  configuration, and from the connect task.
Remove Bower in favor of NPM

Goals:
------

- A single package manager.

Instructions:
-------------

- Move dependencies object from bower.json to package.json.

- Delete bower.json.

- Remove references to debowerify in the connect task, browserify tsak,
  and package.json files.

Extra credit:
-------------

- Update references to make the example work.
Convert to ES6

Goals:
------

- Leverage the emerging standard for JavaScript modules to restructure
  our code.
- Work around limitations with the current ecosystem.

Instructions:
-------------

- Using NPM, Install and save `systemjs` as a dependency.

- Using NPM, install and save: (as dev dependencies)
  - systemjs-builder
  - traceur

- Can safely remove underscore from dependencies.

- Update the `src/.jshintrc` file to contain:

  "esnext": true

  - This will prevent issues with JSHint seeing ES6-syntax

- Copy the `_source/traceur.js` file to our src directory.  This file is
  different from what's in NPM and works correctly with SystemJS.

- Add a `.jshintignore` file in the root containing:

  src/traceur.js

  - This will prevent JSHint from scanning this dependency.

- Remove jscs from the lint task in the Gruntfile.  This will not work
  with ES6 syntax.

- Remove browserify and jscs from watch task.

- Create a SystemJS loader plugin for Lo-Dash templates; store this file
  in `src/plugins/lodash.js`

  'use strict';

  import _ from 'underscore';

  export function translate(load) {
    return 'module.exports = ' + _.template(load.source).source + ';';
  }

- Inside `src/index.jade` update to for the dev target:

  script(src='/src/traceur.js')
  script(src='/node_modules/es6-module-loader/dist/es6-module-loader.js')
  script(src='/node_modules/systemjs/dist/system.js')

  script.
    System.config({
      baseURL: '/',

      paths: {
        backbone: '/node_modules/backbone/backbone.js',
        jquery: '/node_modules/jquery/dist/jquery.js',
        underscore: '/node_modules/lodash/dist/lodash.js',
        tmpl: 'src/plugins/lodash.js'
      }
    });

    System.import('src/main').catch(function(ex) {
      console.log(ex);
    });

- Convert the modules to ES6 syntax.
  - The syntax for a SystemJS loader plugin is 'path!plugin'.
  - We've registered the plugin name as tmpl.
Optmize ES6 with SystemJS

Goals:
------

- Bundle the ES6 modules down to as few files as possible.

Instructions:
-------------

- Remove browserify references in Gruntfile and build folder.

- There is a bug with the systemjs-builder that requires two different
  copies of a plugin loader.  Currently working to address this with the
  author:

  https://github.com/systemjs/builder/issues/22

  - This means we need a lodash-build and a lodash-runtime version.
  - Rename `plugins/lodash.js` to `plugins/lodash-build.js` and update
    the import:

    var _ = require('lodash');

  - This is a weird hybrid of CJS and ES6, but it works for the
    optimizer.
  - Add the following source to the runtime version:

    'use strict';

    import _ from 'lodash';

    export function translate(load) {
      return 'module.exports = ' + _.template(load.source).source + ';';
    }

- Add in a new build task for systemjs.
  - The source for the plugin:

    'use strict';

    var path = require('path');
    var builder = require('systemjs-builder');
    var local = path.join.bind(path, __dirname);

    module.exports = function(grunt) {
      grunt.registerTask('systemjs', function() {
        var done = this.async();

        builder.build('src/main', {
          baseURL: local('../'),

          paths: {
            backbone: 'node_modules/backbone/backbone.js',
            jquery: 'node_modules/jquery/dist/jquery.js',
            underscore: 'node_modules/lodash/dist/lodash.js',
            lodash: 'node_modules/lodash/dist/lodash.js',
            tmpl: 'src/plugins/lodash-build.js'
          }
        }, local('../prod/app.js'))

        .then(done).catch(function(ex) {
          done(new Error(ex));
        });
      });
    };

- Add in an `else` condition for production within `src/index.jade`:

  else
    script(src='/traceur-runtime.js')
    script(src='/es6-module-loader.js')
    script(src='/system.js')
    script(src='/app.js')

  - Unfortunately we still need to manually include the Traceur runtime,
    ES6 Module Loader, and SystemJS in production and separately
    otherwise SystemJS will error.

- Your application should be optimized!