Index: frontend/node_modules/jake/test/integration/concurrent.js
===================================================================
--- frontend/node_modules/jake/test/integration/concurrent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/concurrent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,45 @@
+let assert = require('assert');
+let exec = require('child_process').execSync;
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+suite('concurrent', function () {
+
+  this.timeout(7000);
+
+  test(' simple concurrent prerequisites 1', function () {
+    let out = exec(`${JAKE_CMD} -q concurrent:simple1`).toString().trim();
+    assert.equal('Started A\nStarted B\nFinished B\nFinished A', out);
+  });
+
+  test(' simple concurrent prerequisites 2', function () {
+    let out = exec(`${JAKE_CMD} -q concurrent:simple2`).toString().trim();
+    assert.equal('Started C\nStarted D\nFinished C\nFinished D', out);
+  });
+
+  test(' sequential concurrent prerequisites', function () {
+    let out = exec(`${JAKE_CMD} -q concurrent:seqconcurrent`).toString().trim();
+    assert.equal('Started A\nStarted B\nFinished B\nFinished A\nStarted C\nStarted D\nFinished C\nFinished D', out);
+  });
+
+  test(' concurrent concurrent prerequisites', function () {
+    let out = exec(`${JAKE_CMD} -q concurrent:concurrentconcurrent`).toString().trim();
+    assert.equal('Started A\nStarted B\nStarted C\nStarted D\nFinished B\nFinished C\nFinished A\nFinished D', out);
+  });
+
+  test(' concurrent prerequisites with subdependency', function () {
+    let out = exec(`${JAKE_CMD} -q concurrent:subdep`).toString().trim();
+    assert.equal('Started A\nFinished A\nStarted Ba\nFinished Ba', out);
+  });
+
+  test(' failing in concurrent prerequisites', function () {
+    try {
+      exec(`${JAKE_CMD} -q concurrent:Cfail`);
+    }
+    catch(err) {
+      assert(err.message.indexOf('Command failed') > -1);
+    }
+  });
+
+});
Index: frontend/node_modules/jake/test/integration/file.js
===================================================================
--- frontend/node_modules/jake/test/integration/file.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/file.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,228 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+
+let assert = require('assert');
+let fs = require('fs');
+let path = require('path');
+let file = require(`${PROJECT_DIR}/lib/utils/file`);
+let existsSync = fs.existsSync || path.existsSync;
+let exec = require('child_process').execSync;
+
+suite('fileUtils', function () {
+
+  test('mkdirP', function () {
+    let expected = [
+      ['foo'],
+      ['foo', 'bar'],
+      ['foo', 'bar', 'baz'],
+      ['foo', 'bar', 'baz', 'qux']
+    ];
+    file.mkdirP('foo/bar/baz/qux');
+    let res = exec('find foo').toString().trim().split('\n');
+    for (let i = 0, ii = res.length; i < ii; i++) {
+      assert.equal(path.join.apply(path, expected[i]), res[i]);
+    }
+    file.rmRf('foo');
+  });
+
+  test('rmRf', function () {
+    file.mkdirP('foo/bar/baz/qux');
+    file.rmRf('foo/bar');
+    let res = exec('find foo').toString().trim().split('\n');
+    assert.equal(1, res.length);
+    assert.equal('foo', res[0]);
+    fs.rmdirSync('foo');
+  });
+
+  test('rmRf with symlink subdir', function () {
+    file.mkdirP('foo');
+    file.mkdirP('bar');
+    fs.writeFileSync('foo/hello.txt', 'hello, it\'s me');
+    fs.symlinkSync('../foo', 'bar/foo'); file.rmRf('bar');
+
+    // Make sure the bar directory was successfully deleted
+    let barDeleted = false;
+    try {
+      fs.statSync('bar');
+    } catch(err) {
+      if(err.code == 'ENOENT') {
+        barDeleted = true;
+      }
+    }
+    assert.equal(true, barDeleted);
+
+    // Make sure that the file inside the linked folder wasn't deleted
+    let res = fs.readdirSync('foo');
+    assert.equal(1, res.length);
+    assert.equal('hello.txt', res[0]);
+
+    // Cleanup
+    fs.unlinkSync('foo/hello.txt');
+    fs.rmdirSync('foo');
+  });
+
+  test('rmRf with symlinked dir', function () {
+    file.mkdirP('foo');
+    fs.writeFileSync('foo/hello.txt', 'hello!');
+    fs.symlinkSync('foo', 'bar');
+    file.rmRf('bar');
+
+    // Make sure the bar directory was successfully deleted
+    let barDeleted = false;
+    try {
+      fs.statSync('bar');
+    } catch(err) {
+      if(err.code == 'ENOENT') {
+        barDeleted = true;
+      }
+    }
+    assert.equal(true, barDeleted);
+
+    // Make sure that the file inside the linked folder wasn't deleted
+    let res = fs.readdirSync('foo');
+    assert.equal(1, res.length);
+    assert.equal('hello.txt', res[0]);
+
+    // Cleanup
+    fs.unlinkSync('foo/hello.txt');
+    fs.rmdirSync('foo');
+  });
+
+  test('cpR with same name and different directory', function () {
+    file.mkdirP('foo');
+    fs.writeFileSync('foo/bar.txt', 'w00t');
+    file.cpR('foo', 'bar');
+    assert.ok(existsSync('bar/bar.txt'));
+    file.rmRf('foo');
+    file.rmRf('bar');
+  });
+
+  test('cpR with same to and from will throw', function () {
+    assert.throws(function () {
+      file.cpR('foo.txt', 'foo.txt');
+    });
+  });
+
+  test('cpR rename via copy in directory', function () {
+    file.mkdirP('foo');
+    fs.writeFileSync('foo/bar.txt', 'w00t');
+    file.cpR('foo/bar.txt', 'foo/baz.txt');
+    assert.ok(existsSync('foo/baz.txt'));
+    file.rmRf('foo');
+  });
+
+  test('cpR rename via copy in base', function () {
+    fs.writeFileSync('bar.txt', 'w00t');
+    file.cpR('bar.txt', 'baz.txt');
+    assert.ok(existsSync('baz.txt'));
+    file.rmRf('bar.txt');
+    file.rmRf('baz.txt');
+  });
+
+  test('cpR keeps file mode', function () {
+    fs.writeFileSync('bar.txt', 'w00t', {mode: 0o750});
+    fs.writeFileSync('bar1.txt', 'w00t!', {mode: 0o744});
+    file.cpR('bar.txt', 'baz.txt');
+    file.cpR('bar1.txt', 'baz1.txt');
+
+    assert.ok(existsSync('baz.txt'));
+    assert.ok(existsSync('baz1.txt'));
+    let bazStat = fs.statSync('baz.txt');
+    let bazStat1 = fs.statSync('baz1.txt');
+    assert.equal(0o750, bazStat.mode & 0o7777);
+    assert.equal(0o744, bazStat1.mode & 0o7777);
+
+    file.rmRf('bar.txt');
+    file.rmRf('baz.txt');
+    file.rmRf('bar1.txt');
+    file.rmRf('baz1.txt');
+  });
+
+  test('cpR keeps file mode when overwriting with preserveMode', function () {
+    fs.writeFileSync('bar.txt', 'w00t', {mode: 0o755});
+    fs.writeFileSync('baz.txt', 'w00t!', {mode: 0o744});
+    file.cpR('bar.txt', 'baz.txt', {silent: true, preserveMode: true});
+
+    assert.ok(existsSync('baz.txt'));
+    let bazStat = fs.statSync('baz.txt');
+    assert.equal(0o755, bazStat.mode & 0o777);
+
+    file.rmRf('bar.txt');
+    file.rmRf('baz.txt');
+  });
+
+  test('cpR does not keep file mode when overwriting', function () {
+    fs.writeFileSync('bar.txt', 'w00t', {mode: 0o766});
+    fs.writeFileSync('baz.txt', 'w00t!', {mode: 0o744});
+    file.cpR('bar.txt', 'baz.txt');
+
+    assert.ok(existsSync('baz.txt'));
+    let bazStat = fs.statSync('baz.txt');
+    assert.equal(0o744, bazStat.mode & 0o777);
+
+    file.rmRf('bar.txt');
+    file.rmRf('baz.txt');
+  });
+
+  test('cpR copies file mode recursively', function () {
+    fs.mkdirSync('foo');
+    fs.writeFileSync('foo/bar.txt', 'w00t', {mode: 0o740});
+    file.cpR('foo', 'baz');
+
+    assert.ok(existsSync('baz'));
+    let barStat = fs.statSync('baz/bar.txt');
+    assert.equal(0o740, barStat.mode & 0o777);
+
+    file.rmRf('foo');
+    file.rmRf('baz');
+  });
+
+  test('cpR keeps file mode recursively', function () {
+    fs.mkdirSync('foo');
+    fs.writeFileSync('foo/bar.txt', 'w00t', {mode: 0o740});
+    fs.mkdirSync('baz');
+    fs.mkdirSync('baz/foo');
+    fs.writeFileSync('baz/foo/bar.txt', 'w00t!', {mode: 0o755});
+    file.cpR('foo', 'baz', {silent: true, preserveMode: true});
+
+    assert.ok(existsSync('baz'));
+    let barStat = fs.statSync('baz/foo/bar.txt');
+    assert.equal(0o740, barStat.mode & 0o777);
+
+    file.rmRf('foo');
+    file.rmRf('baz');
+  });
+
+  test('cpR copies directory mode recursively', function () {
+    fs.mkdirSync('foo', 0o755);
+    fs.mkdirSync('foo/bar', 0o700);
+    file.cpR('foo', 'bar');
+
+    assert.ok(existsSync('foo'));
+    let fooBarStat = fs.statSync('bar/bar');
+    assert.equal(0o700, fooBarStat.mode & 0o777);
+
+    file.rmRf('foo');
+    file.rmRf('bar');
+  });
+
+});
+
+
Index: frontend/node_modules/jake/test/integration/file_task.js
===================================================================
--- frontend/node_modules/jake/test/integration/file_task.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/file_task.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,141 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+let assert = require('assert');
+let fs = require('fs');
+let exec = require('child_process').execSync;
+let { rmRf } = require(`${PROJECT_DIR}/lib/jake`);
+
+let cleanUpAndNext = function (callback) {
+  rmRf('./foo', {
+    silent: true
+  });
+  callback && callback();
+};
+
+suite('fileTask', function () {
+  this.timeout(7000);
+
+  setup(function () {
+    cleanUpAndNext();
+  });
+
+  test('where a file-task prereq does not change with --always-make', function () {
+    let out;
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-src1.txt`).toString().trim();
+    assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task',
+      out);
+    out = exec(`${JAKE_CMD} -q -B fileTest:foo/from-src1.txt`).toString().trim();
+    assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task',
+      out);
+    cleanUpAndNext();
+  });
+
+  test('concating two files', function () {
+    let out;
+    out = exec(`${JAKE_CMD} -q fileTest:foo/concat.txt`).toString().trim();
+    assert.equal('fileTest:foo/src1.txt task\ndefault task\nfileTest:foo/src2.txt task\n' +
+          'fileTest:foo/concat.txt task', out);
+    // Check to see the two files got concat'd
+    let data = fs.readFileSync(process.cwd() + '/foo/concat.txt');
+    assert.equal('src1src2', data.toString());
+    cleanUpAndNext();
+  });
+
+  test('where a file-task prereq does not change', function () {
+    let out;
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-src1.txt`).toString().trim();
+    assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task', out);
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-src1.txt`).toString().trim();
+    // Second time should be a no-op
+    assert.equal('', out);
+    cleanUpAndNext();
+  });
+
+  test('where a file-task prereq does change, then does not', function (next) {
+    exec('mkdir -p ./foo');
+    exec('touch ./foo/from-src1.txt');
+    setTimeout(() => {
+      fs.writeFileSync('./foo/src1.txt', '-SRC');
+      // Task should run the first time
+      let out;
+      out = exec(`${JAKE_CMD} -q fileTest:foo/from-src1.txt`).toString().trim();
+      assert.equal('fileTest:foo/from-src1.txt task', out);
+      // Task should not run on subsequent invocation
+      out = exec(`${JAKE_CMD} -q fileTest:foo/from-src1.txt`).toString().trim();
+      assert.equal('', out);
+      cleanUpAndNext(next);
+    }, 1000);
+  });
+
+  test('a preexisting file', function () {
+    let prereqData = 'howdy';
+    exec('mkdir -p ./foo');
+    fs.writeFileSync('foo/prereq.txt', prereqData);
+    let out;
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-prereq.txt`).toString().trim();
+    assert.equal('fileTest:foo/from-prereq.txt task', out);
+    let data = fs.readFileSync(process.cwd() + '/foo/from-prereq.txt');
+    assert.equal(prereqData, data.toString());
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-prereq.txt`).toString().trim();
+    // Second time should be a no-op
+    assert.equal('', out);
+    cleanUpAndNext();
+  });
+
+  test('a preexisting file with --always-make flag', function () {
+    let prereqData = 'howdy';
+    exec('mkdir -p ./foo');
+    fs.writeFileSync('foo/prereq.txt', prereqData);
+    let out;
+    out = exec(`${JAKE_CMD} -q fileTest:foo/from-prereq.txt`).toString().trim();
+    assert.equal('fileTest:foo/from-prereq.txt task', out);
+    let data = fs.readFileSync(process.cwd() + '/foo/from-prereq.txt');
+    assert.equal(prereqData, data.toString());
+    out = exec(`${JAKE_CMD} -q -B fileTest:foo/from-prereq.txt`).toString().trim();
+    assert.equal('fileTest:foo/from-prereq.txt task', out);
+    cleanUpAndNext();
+  });
+
+  test('nested directory-task', function () {
+    exec(`${JAKE_CMD} -q fileTest:foo/bar/baz/bamf.txt`);
+    let data = fs.readFileSync(process.cwd() + '/foo/bar/baz/bamf.txt');
+    assert.equal('w00t', data);
+    cleanUpAndNext();
+  });
+
+  test('partially existing prereqs', function () {
+    /*
+     dependency graph:
+                               /-- foo/output2a.txt --\
+     foo -- foo/output1.txt --+                        +-- output3.txt
+                               \-- foo/output2b.txt --/
+    */
+    // build part of the prereqs
+    exec(`${JAKE_CMD} -q fileTest:foo/output2a.txt`);
+    // verify the final target gets built
+    exec(`${JAKE_CMD} -q fileTest:foo/output3.txt`);
+    let data = fs.readFileSync(process.cwd() + '/foo/output3.txt');
+    assert.equal('w00t', data);
+    cleanUpAndNext();
+  });
+});
+
Index: frontend/node_modules/jake/test/integration/helpers.js
===================================================================
--- frontend/node_modules/jake/test/integration/helpers.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/helpers.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,80 @@
+var exec = require('child_process').exec;
+
+var helpers = new (function () {
+  var _tests;
+  var _names = [];
+  var _name;
+  var _callback;
+  var _runner = function () {
+    if ((_name = _names.shift())) {
+      console.log('Running ' + _name);
+      _tests[_name]();
+    }
+    else {
+      _callback();
+    }
+  };
+
+  this.exec = function () {
+    var args = Array.prototype.slice.call(arguments);
+    var arg;
+    var cmd = args.shift();
+    var opts = {};
+    var callback;
+    // Optional opts/callback or callback/opts
+    while ((arg = args.shift())) {
+      if (typeof arg == 'function') {
+        callback = arg;
+      }
+      else {
+        opts = arg;
+      }
+    }
+
+    cmd += ' --trace';
+    var execOpts = opts.execOpts ? opts.execOpts : {};
+    exec(cmd, execOpts, function (err, stdout, stderr) {
+      var out = helpers.trim(stdout);
+      if (err) {
+        if (opts.breakOnError === false) {
+          return callback(err);
+        }
+        else {
+          throw err;
+        }
+      }
+      if (stderr) {
+        callback(stderr);
+      }
+      else {
+        callback(out);
+      }
+    });
+  };
+
+  this.trim = function (s) {
+    var str = s || '';
+    return str.replace(/^\s*|\s*$/g, '');
+  };
+
+  this.parse = function (s) {
+    var str = s || '';
+    str = helpers.trim(str);
+    str = str.replace(/'/g, '"');
+    return JSON.parse(str);
+  };
+
+  this.run = function (tests, callback) {
+    _tests = tests;
+    _names = Object.keys(tests);
+    _callback = callback;
+    _runner();
+  };
+
+  this.next = function () {
+    _runner();
+  };
+
+})();
+
+module.exports = helpers;
Index: frontend/node_modules/jake/test/integration/jakefile.js
===================================================================
--- frontend/node_modules/jake/test/integration/jakefile.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/jakefile.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,352 @@
+let fs = require('fs');
+let Q = require('q');
+
+desc('The default t.');
+task('default', function () {
+  console.log('default task');
+});
+
+desc('No action.');
+task({'noAction': ['default']});
+
+desc('No action, no prereqs.');
+task('noActionNoPrereqs');
+
+desc('Top-level zerbofrangazoomy task');
+task('zerbofrangazoomy', function () {
+  console.log('Whaaaaaaaa? Ran the zerbofrangazoomy task!');
+});
+
+desc('Task that throws');
+task('throwy', function () {
+  let errorListener = function (err) {
+    console.log('Emitted');
+    console.log(err.toString());
+
+    jake.removeListener('error', errorListener);
+  };
+
+  jake.on('error', errorListener);
+
+  throw new Error('I am bad');
+});
+
+desc('Task that rejects a Promise');
+task('promiseRejecter', function () {
+  const originalOption = jake.program.opts['allow-rejection'];
+
+  const errorListener = function (err) {
+    console.log(err.toString());
+    jake.removeListener('error', errorListener);
+    jake.program.opts['allow-rejection'] = originalOption; // Restore original 'allow-rejection' option
+  };
+  jake.on('error', errorListener);
+
+  jake.program.opts['allow-rejection'] = false; // Do not allow rejection so the rejection is passed to error handlers
+
+  Promise.reject('<promise rejected on purpose>');
+});
+
+desc('Accepts args and env vars.');
+task('argsEnvVars', function () {
+  let res = {
+    args: arguments
+    , env: {
+      foo: process.env.foo
+      , baz: process.env.baz
+    }
+  };
+  console.log(JSON.stringify(res));
+});
+
+namespace('foo', function () {
+  desc('The foo:bar t.');
+  task('bar', function () {
+    if (arguments.length) {
+      console.log('foo:bar[' +
+          Array.prototype.join.call(arguments, ',') +
+          '] task');
+    }
+    else {
+      console.log('foo:bar task');
+    }
+  });
+
+  desc('The foo:baz task, calls foo:bar as a prerequisite.');
+  task('baz', ['foo:bar'], function () {
+    console.log('foo:baz task');
+  });
+
+  desc('The foo:qux task, calls foo:bar with cmdline args as a prerequisite.');
+  task('qux', ['foo:bar[asdf,qwer]'], function () {
+    console.log('foo:qux task');
+  });
+
+  desc('The foo:frang task,`invokes` foo:bar with passed args as a prerequisite.');
+  task('frang', function () {
+    let t = jake.Task['foo:bar'];
+    // Do args pass-through
+    t.invoke.apply(t, arguments);
+    t.on('complete', () => {
+      console.log('foo:frang task');
+    });
+  });
+
+  desc('The foo:zerb task, `executes` foo:bar with passed args as a prerequisite.');
+  task('zerb', function () {
+    let t = jake.Task['foo:bar'];
+    // Do args pass-through
+    t.execute.apply(t, arguments);
+    t.on('complete', () => {
+      console.log('foo:zerb task');
+    });
+  });
+
+  desc('The foo:zoobie task, has no prerequisites.');
+  task('zoobie', function () {
+    console.log('foo:zoobie task');
+  });
+
+  desc('The foo:voom task, run the foo:zoobie task repeatedly.');
+  task('voom', function () {
+    let t = jake.Task['foo:bar'];
+    t.on('complete', function () {
+      console.log('complete');
+    });
+    t.execute.apply(t);
+    t.execute.apply(t);
+  });
+
+  desc('The foo:asdf task, has the same prereq twice.');
+  task('asdf', ['foo:bar', 'foo:baz'], function () {
+    console.log('foo:asdf task');
+  });
+
+});
+
+namespace('bar', function () {
+  desc('The bar:foo task, has no prerequisites, is async, returns Promise which resolves.');
+  task('foo', async function () {
+    return new Promise((resolve, reject) => {
+      console.log('bar:foo task');
+      resolve();
+    });
+  });
+
+  desc('The bar:promise task has no prerequisites, is async, returns Q-based promise.');
+  task('promise', function () {
+    return Q()
+      .then(function () {
+        console.log('bar:promise task');
+        return 123654;
+      });
+  });
+
+  desc('The bar:dependOnpromise task waits for a promise based async test');
+  task('dependOnpromise', ['promise'], function () {
+    console.log('bar:dependOnpromise task saw value', jake.Task["bar:promise"].value);
+  });
+
+  desc('The bar:brokenPromise task is a failing Q-promise based async task.');
+  task('brokenPromise', function () {
+    return Q()
+      .then(function () {
+        throw new Error("nom nom nom");
+      });
+  });
+
+  desc('The bar:bar task, has the async bar:foo task as a prerequisite.');
+  task('bar', ['bar:foo'], function () {
+    console.log('bar:bar task');
+  });
+
+});
+
+namespace('hoge', function () {
+  desc('The hoge:hoge task, has no prerequisites.');
+  task('hoge', function () {
+    console.log('hoge:hoge task');
+  });
+
+  desc('The hoge:piyo task, has no prerequisites.');
+  task('piyo', function () {
+    console.log('hoge:piyo task');
+  });
+
+  desc('The hoge:fuga task, has hoge:hoge and hoge:piyo as prerequisites.');
+  task('fuga', ['hoge:hoge', 'hoge:piyo'], function () {
+    console.log('hoge:fuga task');
+  });
+
+  desc('The hoge:charan task, has hoge:fuga as a prerequisite.');
+  task('charan', ['hoge:fuga'], function () {
+    console.log('hoge:charan task');
+  });
+
+  desc('The hoge:gero task, has hoge:fuga as a prerequisite.');
+  task('gero', ['hoge:fuga'], function () {
+    console.log('hoge:gero task');
+  });
+
+  desc('The hoge:kira task, has hoge:charan and hoge:gero as prerequisites.');
+  task('kira', ['hoge:charan', 'hoge:gero'], function () {
+    console.log('hoge:kira task');
+  });
+
+});
+
+namespace('fileTest', function () {
+  directory('foo');
+
+  desc('File task, concatenating two files together');
+  file('foo/concat.txt', ['fileTest:foo', 'fileTest:foo/src1.txt', 'fileTest:foo/src2.txt'], function () {
+    console.log('fileTest:foo/concat.txt task');
+    let data1 = fs.readFileSync('foo/src1.txt');
+    let data2 = fs.readFileSync('foo/src2.txt');
+    fs.writeFileSync('foo/concat.txt', data1 + data2);
+  });
+
+  desc('File task, async creation with writeFile');
+  file('foo/src1.txt', function () {
+    return new Promise(function (resolve, reject) {
+      fs.writeFile('foo/src1.txt', 'src1', function (err) {
+        if (err) {
+          reject(err);
+        }
+        else {
+          console.log('fileTest:foo/src1.txt task');
+          resolve();
+        }
+      });
+    });
+  });
+
+  desc('File task, sync creation with writeFileSync');
+  file('foo/src2.txt', ['default'], function () {
+    fs.writeFileSync('foo/src2.txt', 'src2');
+    console.log('fileTest:foo/src2.txt task');
+  });
+
+  desc('File task, do not run unless the prereq file changes');
+  file('foo/from-src1.txt', ['fileTest:foo', 'fileTest:foo/src1.txt'], function () {
+    let data = fs.readFileSync('foo/src1.txt').toString();
+    fs.writeFileSync('foo/from-src1.txt', data);
+    console.log('fileTest:foo/from-src1.txt task');
+  });
+
+  desc('File task, run if the prereq file changes');
+  task('touch-prereq', function () {
+    fs.writeFileSync('foo/prereq.txt', 'UPDATED');
+  });
+
+  desc('File task, has a preexisting file (with no associated task) as a prereq');
+  file('foo/from-prereq.txt', ['fileTest:foo', 'foo/prereq.txt'], function () {
+    let data = fs.readFileSync('foo/prereq.txt');
+    fs.writeFileSync('foo/from-prereq.txt', data);
+    console.log('fileTest:foo/from-prereq.txt task');
+  });
+
+  directory('foo/bar/baz');
+
+  desc('Write a file in a nested subdirectory');
+  file('foo/bar/baz/bamf.txt', ['foo/bar/baz'], function () {
+    fs.writeFileSync('foo/bar/baz/bamf.txt', 'w00t');
+  });
+
+  file('foo/output1.txt', ['foo'], () => {
+    fs.writeFileSync('foo/output1.txt', 'Contents of foo/output1.txt');
+  });
+
+  file('foo/output2a.txt', ['foo/output1.txt'], () => {
+    fs.writeFileSync('foo/output2a.txt', 'Contents of foo/output2a.txt');
+  });
+
+  file('foo/output2b.txt', ['foo/output1.txt'], () => {
+    fs.writeFileSync('foo/output2b.txt', 'Contents of foo/output2b.txt');
+  });
+
+  file('foo/output3.txt', [ 'foo/output2a.txt', 'foo/output2b.txt'], () => {
+    fs.writeFileSync('foo/output3.txt', 'w00t');
+  });
+});
+
+task('blammo');
+// Define task
+task('voom', ['blammo'], function () {
+  console.log(this.prereqs.length);
+});
+
+// Modify, add a prereq
+task('voom', ['noActionNoPrereqs']);
+
+namespace('vronk', function () {
+  task('groo', function () {
+    let t = jake.Task['vronk:zong'];
+    t.addListener('error', function (e) {
+      console.log(e.message);
+    });
+    t.invoke();
+  });
+  task('zong', function () {
+    throw new Error('OMFGZONG');
+  });
+});
+
+// define namespace
+namespace('one', function () {
+  task('one', function () {
+    console.log('one:one');
+  });
+});
+
+// modify namespace (add task)
+namespace('one', function () {
+  task('two', ['one:one'], function () {
+    console.log('one:two');
+  });
+});
+
+task('selfdepconst', [], function () {
+  task('selfdep', ['selfdep'], function () {
+    console.log("I made a task that depends on itself");
+  });
+});
+task('selfdepdyn', function () {
+  task('selfdeppar', [], {concurrency: 2}, function () {
+    console.log("I will depend on myself and will fail at runtime");
+  });
+  task('selfdeppar', ['selfdeppar']);
+  jake.Task['selfdeppar'].invoke();
+});
+
+namespace("large", function () {
+  task("leaf", function () {
+    console.log("large:leaf");
+  });
+
+  const same = [];
+  for (let i = 0; i < 2000; i++) {
+    same.push("leaf");
+  }
+
+  desc("Task with a large number of same prereqs");
+  task("same", same, { concurrency: 2 }, function () {
+    console.log("large:same");
+  });
+
+  const different = [];
+  for (let i = 0; i < 2000; i++) {
+    const name = "leaf-" + i;
+    task(name, function () {
+      if (name === "leaf-12" || name === "leaf-123") {
+        console.log(name);
+      }
+    });
+    different.push(name);
+  }
+
+  desc("Task with a large number of different prereqs");
+  task("different", different, { concurrency: 2 } , function () {
+    console.log("large:different");
+  });
+});
Index: frontend/node_modules/jake/test/integration/jakelib/concurrent.jake.js
===================================================================
--- frontend/node_modules/jake/test/integration/jakelib/concurrent.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/jakelib/concurrent.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,113 @@
+
+namespace('concurrent', function () {
+  task('A', function () {
+    console.log('Started A');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Finished A');
+        resolve();
+      }, 200);
+    });
+  });
+
+  task('B', function () {
+    console.log('Started B');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Finished B');
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('C', function () {
+    console.log('Started C');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Finished C');
+        resolve();
+      }, 100);
+    });
+  });
+
+  task('D', function () {
+    console.log('Started D');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Finished D');
+        resolve();
+      }, 300);
+    });
+  });
+
+  task('Ba', ['A'], function () {
+    console.log('Started Ba');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Finished Ba');
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('Afail', function () {
+    console.log('Started failing task');
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        console.log('Failing B with error');
+        throw new Error('I failed');
+      }, 50);
+    });
+  });
+
+  task('simple1', ['A','B'], {concurrency: 2}, function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('simple2', ['C','D'], {concurrency: 2}, function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('seqconcurrent', ['simple1','simple2'], function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('concurrentconcurrent', ['simple1','simple2'], {concurrency: 2}, function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('subdep', ['A','Ba'], {concurrency: 2}, function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+  task('fail', ['A', 'B', 'Afail'], {concurrency: 3}, function () {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve();
+      }, 50);
+    });
+  });
+
+});
+
+
Index: frontend/node_modules/jake/test/integration/jakelib/publish.jake.js
===================================================================
--- frontend/node_modules/jake/test/integration/jakelib/publish.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/jakelib/publish.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,49 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+
+let fs = require('fs');
+let { publishTask, rmRf, mkdirP } = require(`${PROJECT_DIR}/lib/jake`);
+
+fs.writeFileSync('package.json', '{"version": "0.0.1"}');
+mkdirP('tmp_publish');
+fs.writeFileSync('tmp_publish/foo.txt', 'FOO');
+
+publishTask('zerb', function () {
+  this.packageFiles.include([
+    'package.json'
+    , 'tmp_publish/**'
+  ]);
+  this.publishCmd = 'node -p -e "\'%filename\'"';
+  this.gitCmd = 'echo';
+  this.scheduleDelay = 0;
+
+  this._ensureRepoClean = function () {};
+  this._getCurrentBranch = function () {
+    return 'v0.0';
+  };
+});
+
+jake.setTaskTimeout(5000);
+
+jake.Task['publish'].on('complete', function () {
+  rmRf('tmp_publish', {silent: true});
+  rmRf('package.json', {silent: true});
+});
+
Index: frontend/node_modules/jake/test/integration/jakelib/required_module.jake.js
===================================================================
--- frontend/node_modules/jake/test/integration/jakelib/required_module.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/jakelib/required_module.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,10 @@
+let { task, namespace } = require(`${process.env.PROJECT_DIR}/lib/jake`);
+
+namespace('usingRequire', function () {
+  task('test', () => {
+    console.log('howdy test');
+  });
+});
+
+
+
Index: frontend/node_modules/jake/test/integration/jakelib/rule.jake.js
===================================================================
--- frontend/node_modules/jake/test/integration/jakelib/rule.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/jakelib/rule.jake.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,222 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+
+let exec = require('child_process').execSync;
+let fs = require('fs');
+let util = require('util');
+let { rule, rmRf } = require(`${PROJECT_DIR}/lib/jake`);
+
+directory('tmpsrc');
+directory('tmpbin');
+
+////////////////////////////////////////////////////////////
+// Simple Suffix Rule
+file('tmp', ['tmp_init', 'tmp_dep1.o', 'tmp_dep2.o'], function (params) {
+  console.log('tmp task');
+  let data1 = fs.readFileSync('tmp_dep1.o');
+  let data2 = fs.readFileSync('tmp_dep2.o');
+  fs.writeFileSync('tmp', data1 + data2);
+});
+
+rule('.o', '.c', function () {
+  let cmd = util.format('cp %s %s', this.source, this.name);
+  console.log(cmd + ' task');
+  exec(cmd);
+});
+
+file('tmp_dep1.c', function () {
+  fs.writeFileSync('tmp_dep1.c', 'src_1');
+  console.log('tmp_dep1.c task');
+});
+
+// note that tmp_dep2.o depends on tmp_dep2.c, which is a
+// static file.
+task('tmp_init', function () {
+  fs.writeFileSync('tmp_dep2.c', 'src_2');
+  console.log('tmp_dep2.c task');
+});
+////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////
+// Pattern Rule
+file('tmp_p', ['tmp_init', 'tmp_dep1.oo', 'tmp_dep2.oo'], function (params) {
+  console.log('tmp pattern task');
+  let data1 = fs.readFileSync('tmp_dep1.oo');
+  let data2 = fs.readFileSync('tmp_dep2.oo');
+  fs.writeFileSync('tmp_p', data1 + data2 + ' pattern');
+});
+
+rule('%.oo', '%.c', function () {
+  let cmd = util.format('cp %s %s', this.source, this.name);
+  console.log(cmd + ' task');
+  exec(cmd);
+});
+////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////
+// Pattern Rule with Folder
+// i.e.  rule('tmpbin/%.oo', 'tmpsrc/%.c', ...
+file('tmp_pf', [
+  'tmp_src_init'
+  , 'tmpbin'
+  , 'tmpbin/tmp_dep1.oo'
+  , 'tmpbin/tmp_dep2.oo' ], function (params) {
+  console.log('tmp pattern folder task');
+  let data1 = fs.readFileSync('tmpbin/tmp_dep1.oo');
+  let data2 = fs.readFileSync('tmpbin/tmp_dep2.oo');
+  fs.writeFileSync('tmp_pf', data1 + data2 + ' pattern folder');
+});
+
+rule('tmpbin/%.oo', 'tmpsrc/%.c', function () {
+  let cmd = util.format('cp %s %s', this.source, this.name);
+  console.log(cmd + ' task');
+  exec(cmd);
+});
+
+file('tmpsrc/tmp_dep2.c',['tmpsrc'], function () {
+  fs.writeFileSync('tmpsrc/tmp_dep2.c', 'src/src_2');
+  console.log('tmpsrc/tmp_dep2.c task');
+});
+
+// Create static files in folder tmpsrc.
+task('tmp_src_init', ['tmpsrc'], function () {
+  fs.writeFileSync('tmpsrc/tmp_dep1.c', 'src/src_1');
+  console.log('tmpsrc/tmp_dep1.c task');
+});
+////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////
+// Namespace Test. This is a Mixed Test.
+// Test for
+// -  rules belonging to different namespace.
+// -  rules with folder and pattern
+task('tmp_ns', [
+  'tmpbin'
+  , 'rule:init'
+  , 'tmpbin/tmp_dep2.oo'    // *** This relies on a rule defined before.
+  , 'rule:tmpbin/dep1.oo'
+  , 'rule:tmpbin/file2.oo' ], function () {
+  console.log('tmp pattern folder namespace task');
+  let data1 = fs.readFileSync('tmpbin/dep1.oo');
+  let data2 = fs.readFileSync('tmpbin/tmp_dep2.oo');
+  let data3 = fs.readFileSync('tmpbin/file2.oo');
+  fs.writeFileSync('tmp_ns', data1 + data2 + data3 + ' pattern folder namespace');
+});
+
+namespace('rule', function () {
+  task('init', ['tmpsrc'], function () {
+    fs.writeFileSync('tmpsrc/file2.c', 'src/src_3');
+    console.log('tmpsrc/file2.c init task');
+  });
+
+  file('tmpsrc/dep1.c',['tmpsrc'], function () {
+    fs.writeFileSync('tmpsrc/dep1.c', 'src/src_1');
+    console.log('tmpsrc/dep1.c task');
+  }, {async: true});
+
+  rule('tmpbin/%.oo', 'tmpsrc/%.c', function () {
+    let cmd = util.format('cp %s %s', this.source, this.name);
+    console.log(cmd + ' ns task');
+    exec(cmd);
+  });
+});
+////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////
+// Chain rule
+// rule('tmpbin/%.pdf', 'tmpbin/%.dvi', function() { ...
+// rule('tmpbin/%.dvi', 'tmpsrc/%.tex', ['tmpbin'], function() { ...
+task('tmp_cr', [
+  'chainrule:init'
+  , 'chainrule:tmpbin/file1.pdf'
+  , 'chainrule:tmpbin/file2.pdf' ], function () {
+  console.log('tmp chainrule namespace task');
+  let data1 = fs.readFileSync('tmpbin/file1.pdf');
+  let data2 = fs.readFileSync('tmpbin/file2.pdf');
+  fs.writeFileSync('tmp_cr', data1 + data2 + ' chainrule namespace');
+});
+
+namespace('chainrule', function () {
+  task('init', ['tmpsrc', 'tmpbin'], function () {
+    fs.writeFileSync('tmpsrc/file1.tex', 'tex1 ');
+    fs.writeFileSync('tmpsrc/file2.tex', 'tex2 ');
+    console.log('chainrule init task');
+  });
+
+  rule('tmpbin/%.pdf', 'tmpbin/%.dvi', function () {
+    let cmd = util.format('cp %s %s', this.source, this.name);
+    console.log(cmd + ' dvi->pdf task');
+    exec(cmd);
+  });
+
+  rule('tmpbin/%.dvi', 'tmpsrc/%.tex', ['tmpbin'], function () {
+    let cmd = util.format('cp %s %s', this.source, this.name);
+    console.log(cmd + ' tex->dvi task');
+    exec(cmd);
+  });
+});
+////////////////////////////////////////////////////////////
+namespace('precedence', function () {
+  task('test', ['foo.html'], function () {
+    console.log('ran test');
+  });
+
+  rule('.html', '.txt', function () {
+    console.log('created html');
+    let data = fs.readFileSync(this.source);
+    fs.writeFileSync(this.name, data.toString());
+  });
+});
+
+namespace('regexPattern', function () {
+  task('test', ['foo.html'], function () {
+    console.log('ran test');
+  });
+
+  rule(/\.html$/, '.txt', function () {
+    console.log('created html');
+    let data = fs.readFileSync(this.source);
+    fs.writeFileSync(this.name, data.toString());
+  });
+});
+
+namespace('sourceFunction', function () {
+
+  let srcFunc = function (taskName) {
+    return taskName.replace(/\.[^.]+$/, '.txt');
+  };
+
+  task('test', ['foo.html'], function () {
+    console.log('ran test');
+  });
+
+  rule('.html', srcFunc, function () {
+    console.log('created html');
+    let data = fs.readFileSync(this.source);
+    fs.writeFileSync(this.name, data.toString());
+  });
+});
+
+////////////////////////////////////////////////////////////
+task('clean', function () {
+  rmRf('./foo');
+  rmRf('./tmp');
+});
Index: frontend/node_modules/jake/test/integration/list_tasks.js
===================================================================
--- frontend/node_modules/jake/test/integration/list_tasks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/list_tasks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,15 @@
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+let assert = require('assert');
+let proc = require('child_process');
+
+suite('listTasks', function () {
+  test('execute "jake -T" without any errors', function () {
+    let message = 'cannot run "jake -T" command';
+    let listTasks = function () {
+      proc.execFileSync(JAKE_CMD, ['-T']);
+    };
+    assert.doesNotThrow(listTasks, TypeError, message);
+  });
+});
Index: frontend/node_modules/jake/test/integration/publish_task.js
===================================================================
--- frontend/node_modules/jake/test/integration/publish_task.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/publish_task.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,27 @@
+let assert = require('assert');
+let exec = require('child_process').execSync;
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+suite('publishTask', function () {
+
+  this.timeout(7000);
+
+  test('default task', function () {
+    let out = exec(`${JAKE_CMD} -q publish`).toString().trim();
+    let expected = [
+      'Fetched remote tags.'
+      , 'On branch v0.0'
+      , 'Bumped version number to v0.0.2.'
+      , 'Created package for zerb v0.0.2'
+      , 'Publishing zerb v0.0.2'
+      , './pkg/zerb-v0.0.2.tar.gz'
+      , 'BOOM! Published.'
+      , 'Cleaned up package'
+    ].join('\n');
+    assert.equal(expected, out);
+  });
+
+});
+
Index: frontend/node_modules/jake/test/integration/rule.js
===================================================================
--- frontend/node_modules/jake/test/integration/rule.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/rule.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,217 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+let assert = require('assert');
+let exec = require('child_process').execSync;
+let fs = require('fs');
+let { Rule } = require(`${PROJECT_DIR}/lib/rule`);
+let { rmRf } = require(`${PROJECT_DIR}/lib/jake`);
+
+let cleanUpAndNext = function (callback) {
+  // Gotta add globbing to file utils rmRf
+  let tmpFiles = [
+    'tmp'
+    , 'tmp_ns'
+    , 'tmp_cr'
+    , 'tmp_p'
+    , 'tmp_pf'
+    , 'tmpbin'
+    , 'tmpsrc'
+    , 'tmp_dep1.c'
+    , 'tmp_dep1.o'
+    , 'tmp_dep1.oo'
+    , 'tmp_dep2.c'
+    , 'tmp_dep2.o'
+    , 'tmp_dep2.oo'
+    , 'foo'
+    , 'foo.html'
+  ];
+  tmpFiles.forEach(function (f) {
+    rmRf(f, {
+      silent: true
+    });
+  });
+  callback && callback();
+};
+
+suite('rule', function () {
+
+  this.timeout(7000);
+
+  setup(function (next) {
+    cleanUpAndNext(next);
+  });
+
+
+  //  - name   foo:bin/main.o
+  //  - pattern    bin/%.o
+  //  - source    src/%.c
+  //
+  // return {
+  //    'dep' : 'foo:src/main.c',
+  //    'file': 'src/main.c'
+  //  };
+  test('Rule.getSource', function () {
+    let src = Rule.getSource('foo:bin/main.o', 'bin/%.o', 'src/%.c');
+    assert.equal('foo:src/main.c', src);
+  });
+
+  test('rule w/o pattern', function () {
+    let out = exec( `${JAKE_CMD} -q  tmp`).toString().trim();
+    let output = [
+      "tmp_dep2.c task"
+      , "tmp_dep1.c task"
+      , "cp tmp_dep1.c tmp_dep1.o task"
+      , "cp tmp_dep2.c tmp_dep2.o task"
+      , "tmp task"];
+    assert.equal( output.join('\n'), out);
+    let data = fs.readFileSync(process.cwd() + '/tmp');
+    assert.equal('src_1src_2', data.toString());
+    cleanUpAndNext();
+  });
+
+  test('rule w pattern w/o folder w/o namespace', function () {
+    let out = exec( `${JAKE_CMD}  -q  tmp_p`).toString().trim();
+    let output = [
+      "tmp_dep2.c task"
+      , "tmp_dep1.c task"
+      , "cp tmp_dep1.c tmp_dep1.oo task"
+      , "cp tmp_dep2.c tmp_dep2.oo task"
+      , "tmp pattern task"];
+    let data;
+    assert.equal( output.join('\n'), out);
+    data = fs.readFileSync(process.cwd() + '/tmp_p');
+    assert.equal('src_1src_2 pattern', data.toString());
+    cleanUpAndNext();
+  });
+
+  test('rule w pattern w folder w/o namespace', function () {
+    let out = exec( `${JAKE_CMD}  -q  tmp_pf`).toString().trim();
+    let output = [
+      "tmpsrc/tmp_dep1.c task"
+      , "cp tmpsrc/tmp_dep1.c tmpbin/tmp_dep1.oo task"
+      , "tmpsrc/tmp_dep2.c task"
+      , "cp tmpsrc/tmp_dep2.c tmpbin/tmp_dep2.oo task"
+      , "tmp pattern folder task"];
+    let data;
+    assert.equal( output.join('\n'), out);
+    data = fs.readFileSync(process.cwd() + '/tmp_pf');
+    assert.equal('src/src_1src/src_2 pattern folder', data.toString());
+    cleanUpAndNext();
+  });
+
+  test.skip('rule w pattern w folder w namespace', function () {
+    let out = exec( `${JAKE_CMD} -q   tmp_ns`).toString().trim();
+    let output = [
+      "tmpsrc/file2.c init task" // yes
+      , "tmpsrc/tmp_dep2.c task" // no
+      , "cp tmpsrc/tmp_dep2.c tmpbin/tmp_dep2.oo task" // no
+      , "tmpsrc/dep1.c task" // no
+      , "cp tmpsrc/dep1.c tmpbin/dep1.oo ns task" // no
+      , "cp tmpsrc/file2.c tmpbin/file2.oo ns task" // yes
+      , "tmp pattern folder namespace task"]; // yes
+    let data;
+    assert.equal( output.join('\n'), out);
+    data = fs.readFileSync(process.cwd() + '/tmp_ns');
+    assert.equal('src/src_1src/src_2src/src_3 pattern folder namespace', data.toString());
+    cleanUpAndNext();
+  });
+
+  test.skip('rule w chain w pattern w folder w namespace', function () {
+    let out = exec( `${JAKE_CMD} -q tmp_cr`).toString().trim();
+    let output = [
+      "chainrule init task"
+      , "cp tmpsrc/file1.tex tmpbin/file1.dvi tex->dvi task"
+      , "cp tmpbin/file1.dvi tmpbin/file1.pdf dvi->pdf task"
+      , "cp tmpsrc/file2.tex tmpbin/file2.dvi tex->dvi task"
+      , "cp tmpbin/file2.dvi tmpbin/file2.pdf dvi->pdf task"
+      , "tmp chainrule namespace task"];
+    let data;
+    assert.equal( output.join('\n'), out);
+    data = fs.readFileSync(process.cwd() + '/tmp_cr');
+    assert.equal('tex1 tex2  chainrule namespace', data.toString());
+    cleanUpAndNext();
+  });
+
+
+  ['precedence', 'regexPattern', 'sourceFunction'].forEach(function (key) {
+
+    test('rule with source file not created yet (' + key  + ')', function () {
+      let write = process.stderr.write;
+      process.stderr.write = () => {};
+      rmRf('foo.txt', {silent: true});
+      rmRf('foo.html', {silent: true});
+      try {
+        exec(`${JAKE_CMD}  ` + key + ':test');
+      }
+      catch(err) {
+        // foo.txt prereq doesn't exist yet
+        assert.ok(err.message.indexOf('Unknown task "foo.html"') > -1);
+      }
+      process.stderr.write = write;
+    });
+
+    test('rule with source file now created (' + key  + ')', function () {
+      fs.writeFileSync('foo.txt', '');
+      let out = exec(`${JAKE_CMD} -q  ` + key + ':test').toString().trim();
+      // Should run prereq and test task
+      let output = [
+        'created html'
+        , 'ran test'
+      ];
+      assert.equal(output.join('\n'), out);
+    });
+
+    test('rule with source file modified (' + key  + ')', function (next) {
+      setTimeout(function () {
+        fs.writeFileSync('foo.txt', '');
+        let out = exec(`${JAKE_CMD} -q  ` + key + ':test').toString().trim();
+        // Should again run both prereq and test task
+        let output = [
+          'created html'
+          , 'ran test'
+        ];
+        assert.equal(output.join('\n'), out);
+        //next();
+        cleanUpAndNext(next);
+      }, 1000); // Wait to do the touch to ensure mod-time is different
+    });
+
+    test('rule with existing objective file and no source ' +
+        ' (should be normal file-task) (' + key  + ')', function () {
+      // Remove just the source file
+      fs.writeFileSync('foo.html', '');
+      rmRf('foo.txt', {silent: true});
+      let out = exec(`${JAKE_CMD} -q  ` + key + ':test').toString().trim();
+      // Should treat existing objective file as plain file-task,
+      // and just run test-task
+      let output = [
+        'ran test'
+      ];
+      assert.equal(output.join('\n'), out);
+      cleanUpAndNext();
+    });
+
+  });
+
+});
+
+
Index: frontend/node_modules/jake/test/integration/selfdep.js
===================================================================
--- frontend/node_modules/jake/test/integration/selfdep.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/selfdep.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,42 @@
+let assert = require('assert');
+let exec = require('child_process').execSync;
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+suite('selfDep', function () {
+
+  this.timeout(7000);
+
+  let origStderrWrite;
+
+  setup(function () {
+    origStderrWrite = process.stderr.write;
+    process.stderr.write = function () {};
+  });
+
+  teardown(function () {
+    process.stderr.write = origStderrWrite;
+  });
+
+  test('self dep const', function () {
+    try {
+      exec(`${JAKE_CMD} selfdepconst`);
+    }
+    catch(e) {
+      assert(e.message.indexOf('dependency of itself') > -1);
+    }
+  });
+
+  test('self dep dyn', function () {
+    try {
+      exec(`${JAKE_CMD} selfdepdyn`);
+    }
+    catch(e) {
+      assert(e.message.indexOf('dependency of itself') > -1);
+    }
+  });
+
+});
+
+
Index: frontend/node_modules/jake/test/integration/task_base.js
===================================================================
--- frontend/node_modules/jake/test/integration/task_base.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/integration/task_base.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,167 @@
+let assert = require('assert');
+let h = require('./helpers');
+let exec = require('child_process').execSync;
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+const JAKE_CMD = `${PROJECT_DIR}/bin/cli.js`;
+
+suite('taskBase', function () {
+
+  this.timeout(7000);
+
+  test('default task', function () {
+    let out;
+    out = exec(`${JAKE_CMD} -q`).toString().trim();
+    assert.equal(out, 'default task');
+    out = exec(`${JAKE_CMD} -q default`).toString().trim();
+    assert.equal(out, 'default task');
+  });
+
+  test('task with no action', function () {
+    let out = exec(`${JAKE_CMD} -q noAction`).toString().trim();
+    assert.equal(out, 'default task');
+  });
+
+  test('a task with no action and no prereqs', function () {
+    exec(`${JAKE_CMD} noActionNoPrereqs`);
+  });
+
+  test('a task that exists at the top-level, and not in the specified namespace, should error', function () {
+    let res = require('child_process').spawnSync(`${JAKE_CMD}`,
+      ['asdfasdfasdf:zerbofrangazoomy']);
+    let err = res.stderr.toString();
+    assert.ok(err.indexOf('Unknown task' > -1));
+  });
+
+  test('passing args to a task', function () {
+    let out = exec(`${JAKE_CMD} -q argsEnvVars[foo,bar]`).toString().trim();
+    let parsed = h.parse(out);
+    let args = parsed.args;
+    assert.equal(args[0], 'foo');
+    assert.equal(args[1], 'bar');
+  });
+
+  test('a task with environment vars', function () {
+    let out = exec(`${JAKE_CMD} -q argsEnvVars foo=bar baz=qux`).toString().trim();
+    let parsed = h.parse(out);
+    let env = parsed.env;
+    assert.equal(env.foo, 'bar');
+    assert.equal(env.baz, 'qux');
+  });
+
+  test('passing args and using environment vars', function () {
+    let out = exec(`${JAKE_CMD} -q argsEnvVars[foo,bar] foo=bar baz=qux`).toString().trim();
+    let parsed = h.parse(out);
+    let args = parsed.args;
+    let env = parsed.env;
+    assert.equal(args[0], 'foo');
+    assert.equal(args[1], 'bar');
+    assert.equal(env.foo, 'bar');
+    assert.equal(env.baz, 'qux');
+  });
+
+  test('a simple prereq', function () {
+    let out = exec(`${JAKE_CMD} -q foo:baz`).toString().trim();
+    assert.equal(out, 'foo:bar task\nfoo:baz task');
+  });
+
+  test('a duplicate prereq only runs once', function () {
+    let out = exec(`${JAKE_CMD} -q foo:asdf`).toString().trim();
+    assert.equal(out, 'foo:bar task\nfoo:baz task\nfoo:asdf task');
+  });
+
+  test('a prereq with command-line args', function () {
+    let out = exec(`${JAKE_CMD} -q foo:qux`).toString().trim();
+    assert.equal(out, 'foo:bar[asdf,qwer] task\nfoo:qux task');
+  });
+
+  test('a prereq with args via invoke', function () {
+    let out = exec(`${JAKE_CMD} -q foo:frang[zxcv,uiop]`).toString().trim();
+    assert.equal(out, 'foo:bar[zxcv,uiop] task\nfoo:frang task');
+  });
+
+  test('a prereq with args via execute', function () {
+    let out = exec(`${JAKE_CMD} -q foo:zerb[zxcv,uiop]`).toString().trim();
+    assert.equal(out, 'foo:bar[zxcv,uiop] task\nfoo:zerb task');
+  });
+
+  test('repeating the task via execute', function () {
+    let out = exec(`${JAKE_CMD} -q foo:voom`).toString().trim();
+    assert.equal(out, 'foo:bar task\nfoo:bar task\ncomplete\ncomplete');
+  });
+
+  test('prereq execution-order', function () {
+    let out = exec(`${JAKE_CMD} -q hoge:fuga`).toString().trim();
+    assert.equal(out, 'hoge:hoge task\nhoge:piyo task\nhoge:fuga task');
+  });
+
+  test('basic async task', function () {
+    let out = exec(`${JAKE_CMD} -q bar:bar`).toString().trim();
+    assert.equal(out, 'bar:foo task\nbar:bar task');
+  });
+
+  test('promise async task', function () {
+    let out = exec(`${JAKE_CMD} -q bar:dependOnpromise`).toString().trim();
+    assert.equal(out, 'bar:promise task\nbar:dependOnpromise task saw value 123654');
+  });
+
+  test('failing promise async task', function () {
+    try {
+      exec(`${JAKE_CMD} -q bar:brokenPromise`);
+    }
+    catch(e) {
+      assert(e.message.indexOf('Command failed') > -1);
+    }
+  });
+
+  test('that current-prereq index gets reset', function () {
+    let out = exec(`${JAKE_CMD} -q hoge:kira`).toString().trim();
+    assert.equal(out, 'hoge:hoge task\nhoge:piyo task\nhoge:fuga task\n' +
+        'hoge:charan task\nhoge:gero task\nhoge:kira task');
+  });
+
+  test('modifying a task by adding prereq during execution', function () {
+    let out = exec(`${JAKE_CMD} -q voom`).toString().trim();
+    assert.equal(out, 2);
+  });
+
+  test('listening for task error-event', function () {
+    try {
+      exec(`${JAKE_CMD} -q vronk:groo`).toString().trim();
+    }
+    catch(e) {
+      assert(e.message.indexOf('OMFGZONG') > -1);
+    }
+  });
+
+  test('listening for jake error-event', function () {
+    let out = exec(`${JAKE_CMD} -q throwy`).toString().trim();
+    assert(out.indexOf('Emitted\nError: I am bad') > -1);
+  });
+
+  test('listening for jake unhandledRejection-event', function () {
+    let out = exec(`${JAKE_CMD} -q promiseRejecter`).toString().trim();
+    assert.equal(out, '<promise rejected on purpose>');
+  });
+
+  test('large number of same prereqs', function () {
+    let out = exec(`${JAKE_CMD} -q large:same`).toString().trim();
+    assert.equal(out, 'large:leaf\nlarge:same');
+  });
+
+  test('large number of different prereqs', function () {
+    let out = exec(`${JAKE_CMD} -q large:different`).toString().trim();
+    assert.equal(out, 'leaf-12\nleaf-123\nlarge:different');
+  });
+
+  test('large number of different prereqs', function () {
+    let out = exec(`${JAKE_CMD} -q usingRequire:test`).toString().trim();
+    assert.equal(out, 'howdy test');
+  });
+
+  test('modifying a namespace by adding a new task', function () {
+    let out = exec(`${JAKE_CMD} -q one:two`).toString().trim();
+    assert.equal('one:one\none:two', out);
+  });
+
+});
Index: frontend/node_modules/jake/test/unit/jakefile.js
===================================================================
--- frontend/node_modules/jake/test/unit/jakefile.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/unit/jakefile.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,36 @@
+
+task('foo', function () {
+  console.log('ran top-level foo');
+});
+
+task('bar', function () {
+  console.log('ran top-level bar');
+});
+
+task('zerb', function () {
+  console.log('ran zerb');
+});
+
+namespace('zooby', function () {
+  task('zerp', function () {});
+
+  task('derp', ['zerp'], function () {});
+
+  namespace('frang', function () {
+
+    namespace('w00t', function () {
+      task('bar', function () {
+        console.log('ran zooby:frang:w00t:bar');
+      });
+    });
+
+    task('asdf', function () {});
+  });
+
+});
+
+namespace('hurr', function () {
+  namespace('durr');
+});
+
+
Index: frontend/node_modules/jake/test/unit/namespace.js
===================================================================
--- frontend/node_modules/jake/test/unit/namespace.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/unit/namespace.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,77 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+
+// Load the jake global
+require(`${PROJECT_DIR}/lib/jake`);
+let { Namespace } = require(`${PROJECT_DIR}/lib/namespace`);
+
+require('./jakefile');
+
+let assert = require('assert');
+
+suite('namespace', function () {
+
+  this.timeout(7000);
+
+  test('resolve namespace by relative name', function () {
+    let aaa, bbb, ccc;
+    aaa = namespace('aaa', function () {
+      bbb = namespace('bbb', function () {
+        ccc = namespace('ccc', function () {
+        });
+      });
+    });
+
+    assert.ok(aaa, Namespace.ROOT_NAMESPACE.resolveNamespace('aaa'));
+    assert.ok(bbb === aaa.resolveNamespace('bbb'));
+    assert.ok(ccc === aaa.resolveNamespace('bbb:ccc'));
+  });
+
+  test('resolve task in sub-namespace by relative path', function () {
+    let curr = Namespace.ROOT_NAMESPACE.resolveNamespace('zooby');
+    let task = curr.resolveTask('frang:w00t:bar');
+    assert.ok(task.action.toString().indexOf('zooby:frang:w00t:bar') > -1);
+  });
+
+  test('prefer local to top-level', function () {
+    let curr = Namespace.ROOT_NAMESPACE.resolveNamespace('zooby:frang:w00t');
+    let task = curr.resolveTask('bar');
+    assert.ok(task.action.toString().indexOf('zooby:frang:w00t:bar') > -1);
+  });
+
+  test('does resolve top-level', function () {
+    let curr = Namespace.ROOT_NAMESPACE.resolveNamespace('zooby:frang:w00t');
+    let task = curr.resolveTask('foo');
+    assert.ok(task.action.toString().indexOf('top-level foo') > -1);
+  });
+
+  test('absolute lookup works from sub-namespaces', function () {
+    let curr = Namespace.ROOT_NAMESPACE.resolveNamespace('hurr:durr');
+    let task = curr.resolveTask('zooby:frang:w00t:bar');
+    assert.ok(task.action.toString().indexOf('zooby:frang:w00t:bar') > -1);
+  });
+
+  test('resolution miss with throw error', function () {
+    let curr = Namespace.ROOT_NAMESPACE;
+    let task = curr.resolveTask('asdf:qwer');
+    assert.ok(!task);
+  });
+
+});
Index: frontend/node_modules/jake/test/unit/parseargs.js
===================================================================
--- frontend/node_modules/jake/test/unit/parseargs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/jake/test/unit/parseargs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,169 @@
+/*
+ * Jake JavaScript build tool
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+const PROJECT_DIR = process.env.PROJECT_DIR;
+
+let parseargs = require(`${PROJECT_DIR}/lib/parseargs`);
+let assert = require('assert');
+let optsReg = [
+  { full: 'directory',
+    abbr: 'C',
+    preempts: false,
+    expectValue: true
+  },
+  { full: 'jakefile',
+    abbr: 'f',
+    preempts: false,
+    expectValue: true
+  },
+  { full: 'tasks',
+    abbr: 'T',
+    preempts: true
+  },
+  { full: 'tasks',
+    abbr: 'ls',
+    preempts: true
+  },
+  { full: 'trace',
+    abbr: 't',
+    preempts: false,
+    expectValue: false
+  },
+  { full: 'help',
+    abbr: 'h',
+    preempts: true
+  },
+  { full: 'version',
+    abbr: 'V',
+    preempts: true
+  }
+];
+let p = new parseargs.Parser(optsReg);
+let z = function (s) { return s.split(' '); };
+let res;
+
+suite('parseargs', function () {
+
+  test('long preemptive opt and val with equal-sign, ignore further opts', function () {
+    res = p.parse(z('--tasks=foo --jakefile=asdf'));
+    assert.equal('foo', res.opts.tasks);
+    assert.equal(undefined, res.opts.jakefile);
+  });
+
+  test('long preemptive opt and val without equal-sign, ignore further opts', function () {
+    res = p.parse(z('--tasks foo --jakefile=asdf'));
+    assert.equal('foo', res.opts.tasks);
+    assert.equal(undefined, res.opts.jakefile);
+  });
+
+  test('long preemptive opt and no val, ignore further opts', function () {
+    res = p.parse(z('--tasks --jakefile=asdf'));
+    assert.equal(true, res.opts.tasks);
+    assert.equal(undefined, res.opts.jakefile);
+  });
+
+  test('preemptive opt with no val, should be true', function () {
+    res = p.parse(z('-T'));
+    assert.equal(true, res.opts.tasks);
+  });
+
+  test('preemptive opt with no val, should be true and ignore further opts', function () {
+    res = p.parse(z('-T -f'));
+    assert.equal(true, res.opts.tasks);
+    assert.equal(undefined, res.opts.jakefile);
+  });
+
+  test('preemptive opt with val, should be val', function () {
+    res = p.parse(z('-T zoobie -f foo/bar/baz'));
+    assert.equal('zoobie', res.opts.tasks);
+    assert.equal(undefined, res.opts.jakefile);
+  });
+
+  test('-f expects a value, -t does not (howdy is task-name)', function () {
+    res = p.parse(z('-f zoobie -t howdy'));
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal(true, res.opts.trace);
+    assert.equal('howdy', res.taskNames[0]);
+  });
+
+  test('different order, -f expects a value, -t does not (howdy is task-name)', function () {
+    res = p.parse(z('-f zoobie howdy -t'));
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal(true, res.opts.trace);
+    assert.equal('howdy', res.taskNames[0]);
+  });
+
+  test('-f expects a value, -t does not (foo=bar is env var)', function () {
+    res = p.parse(z('-f zoobie -t foo=bar'));
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal(true, res.opts.trace);
+    assert.equal('bar', res.envVars.foo);
+    assert.equal(undefined, res.taskNames[0]);
+  });
+
+  test('-f expects a value, -t does not (foo=bar is env-var, task-name follows)', function () {
+    res = p.parse(z('-f zoobie -t howdy foo=bar'));
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal(true, res.opts.trace);
+    assert.equal('bar', res.envVars.foo);
+    assert.equal('howdy', res.taskNames[0]);
+  });
+
+  test('-t does not expect a value, -f does (howdy is task-name)', function () {
+    res = p.parse(z('-t howdy -f zoobie'));
+    assert.equal(true, res.opts.trace);
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal('howdy', res.taskNames[0]);
+  });
+
+  test('--trace does not expect a value, -f does (howdy is task-name)', function () {
+    res = p.parse(z('--trace howdy --jakefile zoobie'));
+    assert.equal(true, res.opts.trace);
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal('howdy', res.taskNames[0]);
+  });
+
+  test('--trace does not expect a value (equal), -f does (throw howdy away)', function () {
+    res = p.parse(z('--trace=howdy --jakefile=zoobie'));
+    assert.equal(true, res.opts.trace);
+    assert.equal('zoobie', res.opts.jakefile);
+    assert.equal(undefined, res.taskNames[0]);
+  });
+
+  /*
+, test('task-name with positional args', function () {
+    res = p.parse(z('foo:bar[asdf,qwer]'));
+    assert.equal('asdf', p.taskArgs[0]);
+    assert.equal('qwer', p.taskArgs[1]);
+  }
+
+, test('opts, env vars, task-name with positional args', function () {
+    res = p.parse(z('-f ./tests/Jakefile -t default[asdf,qwer] foo=bar'));
+    assert.equal('./tests/Jakefile', res.opts.jakefile);
+    assert.equal(true, res.opts.trace);
+    assert.equal('bar', res.envVars.foo);
+    assert.equal('default', res.taskName);
+    assert.equal('asdf', p.taskArgs[0]);
+    assert.equal('qwer', p.taskArgs[1]);
+  }
+*/
+
+
+});
+
+
