<commit_before>#!/usr/bin/env node

require('proof')(2, function (step, deepEqual) {
  var fs = require('fs'), cadence = require('../..');

  cadence(function (step) {

    echo(1, step(step, function (number) {
      echo(- number, step());
    }));

  }, function (items) {

    deepEqual(items, -1, 'fixup cadence');

  })(step());

  cadence(function (step) {

    var numbers = step(step, [], function (number) { return - number });
    [ 1, 2, 3 ].forEach(function (number) {
      echo(number, numbers());
    });

  }, function (numbers) {

    deepEqual(numbers, [ -1, -2, -3 ], 'fixup array cadence');

  })(step());
});

function echo (value, callback) { process.nextTick(function () { callback(null, value) }) }
<commit_msg>Test fix-up cadence error reporting.<commit_after>#!/usr/bin/env node

require('proof')(3, function (step, equal, deepEqual) {
  var fs = require('fs'), cadence = require('../..');

  cadence(function (step) {

    echo(1, step(step, function (number) {
      echo(- number, step());
    }));

  }, function (items) {

    deepEqual(items, -1, 'fixup cadence');

  })(step());

  cadence(function (step) {

    echo(1, step(step, function (number) {
      throw new Error('thrown');
      echo(- number, step());
    }));

  })(function (error) {
    equal(error.message, 'thrown', 'errors');
  });

  cadence(function (step) {

    var numbers = step(step, [], function (number) { return - number });
    [ 1, 2, 3 ].forEach(function (number) {
      echo(number, numbers());
    });

  }, function (numbers) {

    deepEqual(numbers, [ -1, -2, -3 ], 'fixup array cadence');

  })(step());
});

function echo (value, callback) { process.nextTick(function () { callback(null, value) }) }
<|endoftext|>


<commit_before>/*
 * grunt-compare-size
 * https://github.com/rwldrn/grunt-compare-size
 *
 * Copyright (c) 2012 Rick Waldron <waldron.rick@gmail.com> &
 *                     Richard Gibson <richard.gibson@gmail.com> &
 *                      Corey Frang <gnarf@gnarf.net> &
 *                       Mike Sherov <mike.sherov@gmail.com>
 * Licensed under the MIT license.
 */

 "use strict";

var fs = require("fs"),
  gzip = require("gzip-js");

module.exports = function(grunt) {
  // Grunt utilities & task-wide assignments
  var file, utils, log, verbose, sizecache, lastrun, helpers;

  file = grunt.file;
  utils = grunt.util;
  log = grunt.log;
  verbose = grunt.verbose;
  sizecache = "dist/.sizecache.json";
  lastrun = " last run";
  helpers = {

    // Label sequence helper
    sorted_labels: function( cache ) {
      var tips = cache[""].tips;

      // Sort labels: metadata, then branch tips by first add,
      // then user entries by first add, then last run
      // Then return without metadata
      return Object.keys( cache ).sort(function( a, b ) {
        var keys = Object.keys( cache );

        return ( a ? 1 : 0 ) - ( b ? 1 : 0 ) ||
          ( a in tips ? 0 : 1 ) - ( b in tips ? 0 : 1 ) ||
          ( a.charAt(0) === " " ? 1 : 0 ) - ( b.charAt(0) === " " ? 1 : 0 ) ||
          keys.indexOf( a ) - keys.indexOf( b );
      }).slice( 1 );
    },

    // Size cache helper
    get_cache: function( src ) {
      var cache;

      try {
        cache = fs.existsSync( src ) ? file.readJSON( src ) : undefined;
      } catch ( e ) {
        verbose.error( e );
      }

      // Progressively upgrade `cache`, which is one of:
      // empty
      // {}
      // { file: size [,...] }
      // { "": { file: size [,...] } [,...] }
      if ( typeof cache !== "object" ) {
        cache = undefined;
      }
      if ( !cache || !cache[""] ) {
        // If promoting to dictionary, assume that data are for last run
        cache = { "": { tips: {} }, " last run": cache };
      }

      return cache;
    },

    // Files helper.
    sizes: function( task ) {
      task.requiresConfig( task.name );

      var sizes = {},
          files = file.expand(
            { filter: "isFile" },
            grunt.config( task.name ).files
          );

      files.forEach(function( src, index ) {
        var contents = file.read( src );
        sizes[ src ] = contents.length;
        if ( index === ( files.length - 1 ) ) {
          sizes[ src + ".gz" ] = gzip.zip( contents, {} ).length;
        }
      });

      return sizes;
    },

    // git helper.
    git_status: function( done ) {
      verbose.write( "Running `git branch` command..." );
      utils.spawn({
        cmd: "git",
        args: [
          "branch", "--no-color", "--verbose", "--no-abbrev", "--contains",
          "HEAD"
        ]
      }, function( err, result ) {
        var status = {},
            matches = /^\* (.+?)\s+([0-9a-f]{8,})/im.exec( result );

        if ( err || !matches ) {
          verbose.error();
          done( err || "branch not found" );
        } else if ( matches[ 1 ].indexOf(" ") >= 0 ) {
          done( "not a branch tip: " + matches[ 2 ] );
        } else {
          status.branch = matches[ 1 ];
          status.head = matches[ 2 ];
          utils.spawn({
            cmd: "git",
            args: [
              "diff", "--quiet", "HEAD"
            ]
          }, function( err, result, code ) {
            status.changed = code !== 0;
            done( null, status );
          });
        }
      });
    }
  };

  // Load test harness, if there is one
  // A hack, but we can't drop it into tasks/ because loadTasks might evaluate the harness first
  if ( fs.existsSync("./harness") ) {
    helpers.git_status = require("../harness/harness");
  }

  // Compare size to saved sizes
  // Derived and adapted from Corey Frang's original `sizer`
  grunt.registerTask( "compare_size", "Compare working size to saved sizes", function() {
    var done = this.async(),
      newsizes = helpers.sizes( this ),
      cache = helpers.get_cache( sizecache ),
      tips = cache[""].tips,
      labels = helpers.sorted_labels( cache );

    // Obtain the current branch and continue...
    helpers.git_status( function( err, status ) {
      if ( err ) {
        log.warn( err );
        status = {};
      }

      labels.forEach(function( label, index ) {
        var key, diff, color,
            oldsizes = cache[ label ];

        // Skip metadata key
        if ( label === "" ) {
          return;
        }

        // Output header line
        log.write(
          !oldsizes ?
            "Sizes" :
            "Sizes - compared to " +
            ( label[0] === " " ?
              label.slice( 1 ) :
              label )
        );

        if ( label in tips ) {
          log.write( " " + ( "@ " + tips[ label ] )[ "grey" ] );
        }
        log.writeln("");

        // Output size comparisons
        for ( key in newsizes ) {
          diff = oldsizes && oldsizes[ key ] && ( newsizes[ key ] - oldsizes[ key ] );

          if ( diff < 0 ) {
            color = "green";
          } else if ( diff > 0 ) {
            diff = "+" + diff;
            color = "red";
          } else {
            diff = "-";
            color = "grey";
          }

          log.writetableln([ 12, 12, 55 ], [
            utils._.lpad( newsizes[ key ], 10 ) ,
            utils._.lpad( "(" + diff + ")", 10 )[ color ],
            key
          ]);
        }

        // Output blank line for following comparisons
        if ( labels.length > index + 1 ) {
          log.writeln("");
        }
      });

      // Update "last run" sizes
      cache[ lastrun ] = newsizes;

      // Remember if we're at a branch tip and the branch name is an available key
      if ( status.branch && !status.changed && ( status.branch in tips || !cache[ status.branch ] ) ) {
        tips[ status.branch ] = status.head;
        cache[ status.branch ] = newsizes;
        log.writeln( "\nSaved as: " + status.branch );
      }

      // Write to file
      file.write( sizecache, JSON.stringify( cache ) );

      done();
    });

    // Fail task if errors were logged.
    if ( this.errorCount ) {
      return false;
    }
  });

  // List saved sizes
  grunt.registerTask( "compare_size_list", "List saved sizes", function() {
    var cache = helpers.get_cache( sizecache ),
        tips = cache[""].tips;

    helpers.sorted_labels( cache ).forEach(function( label ) {
      // Skip the special labels
      if ( label && label.charAt( 0 ) !== " " ) {
        log.write( label );
        if ( label in tips ) {
          log.write( " " + ( "@ " + tips[ label ] )[ "grey" ] );
        }
        log.writeln("");
      }
    });
  });

  // Add custom label
  grunt.registerTask( "compare_size_add", "Add to saved sizes", function() {
    var label,
        cache = helpers.get_cache( sizecache );

    if ( !cache[ lastrun ] ) {
      log.error("No size data found");
      return false;
    }

    // Store last run sizes under each label, clearing them as branch heads
    for ( label in this.flags ) {
      if ( label in cache[""].tips ) {
        delete cache[""].tips[ label ];
        log.write("(removed branch data) ");
      }
      cache[ label ] = cache[ lastrun ];
      log.writeln( "Last run saved as: " + label );
    }

    file.write( sizecache, JSON.stringify( cache ) );
  });

  // Remove custom label
  grunt.registerTask( "compare_size_remove", "Remove from saved sizes", function() {
    var label,
        cache = helpers.get_cache( sizecache );

    for ( label in this.flags ) {
      delete cache[""].tips[ label ];
      delete cache[ label ];
      log.writeln( "Removed: " + label );
    }

    file.write( sizecache, JSON.stringify( cache ) );
  });

  // Empty size cache
  grunt.registerTask( "compare_size_empty", "Clear all saved sizes", function() {
    if ( fs.existsSync( sizecache ) ) {
      fs.unlinkSync( sizecache );
    }
  });
};
<commit_msg>Fix gh-13: Restore MultiTask implementation. Close gh-16.<commit_after>/*
 * grunt-compare-size
 * https://github.com/rwldrn/grunt-compare-size
 *
 * Copyright (c) 2012 Rick Waldron <waldron.rick@gmail.com> &
 *                     Richard Gibson <richard.gibson@gmail.com> &
 *                      Corey Frang <gnarf@gnarf.net> &
 *                       Mike Sherov <mike.sherov@gmail.com>
 * Licensed under the MIT license.
 */

 "use strict";

var fs = require("fs"),
  gzip = require("gzip-js");

module.exports = function(grunt) {
  // Grunt utilities & task-wide assignments
  var file, utils, log, verbose, sizecache, lastrun, helpers;

  file = grunt.file;
  utils = grunt.util;
  log = grunt.log;
  verbose = grunt.verbose;
  sizecache = "dist/.sizecache.json";
  lastrun = " last run";
  helpers = {

    // Label sequence helper
    sorted_labels: function( cache ) {
      var tips = cache[""].tips;

      // Sort labels: metadata, then branch tips by first add,
      // then user entries by first add, then last run
      // Then return without metadata
      return Object.keys( cache ).sort(function( a, b ) {
        var keys = Object.keys( cache );

        return ( a ? 1 : 0 ) - ( b ? 1 : 0 ) ||
          ( a in tips ? 0 : 1 ) - ( b in tips ? 0 : 1 ) ||
          ( a.charAt(0) === " " ? 1 : 0 ) - ( b.charAt(0) === " " ? 1 : 0 ) ||
          keys.indexOf( a ) - keys.indexOf( b );
      }).slice( 1 );
    },

    // Size cache helper
    get_cache: function( src ) {
      var cache;

      try {
        cache = fs.existsSync( src ) ? file.readJSON( src ) : undefined;
      } catch ( e ) {
        verbose.error( e );
      }

      // Progressively upgrade `cache`, which is one of:
      // empty
      // {}
      // { file: size [,...] }
      // { "": { file: size [,...] } [,...] }
      if ( typeof cache !== "object" ) {
        cache = undefined;
      }
      if ( !cache || !cache[""] ) {
        // If promoting to dictionary, assume that data are for last run
        cache = { "": { tips: {} }, " last run": cache };
      }

      return cache;
    },

    // Files helper.
    sizes: function( task ) {
      var sizes = {},
          files = file.expand(
            { filter: "isFile" },
            task.filesSrc
          );

      files.forEach(function( src, index ) {
        var contents = file.read( src );
        sizes[ src ] = contents.length;
        if ( index === ( files.length - 1 ) ) {
          sizes[ src + ".gz" ] = gzip.zip( contents, {} ).length;
        }
      });

      return sizes;
    },

    // git helper.
    git_status: function( done ) {
      verbose.write( "Running `git branch` command..." );
      utils.spawn({
        cmd: "git",
        args: [
          "branch", "--no-color", "--verbose", "--no-abbrev", "--contains",
          "HEAD"
        ]
      }, function( err, result ) {
        var status = {},
            matches = /^\* (.+?)\s+([0-9a-f]{8,})/im.exec( result );

        if ( err || !matches ) {
          verbose.error();
          done( err || "branch not found" );
        } else if ( matches[ 1 ].indexOf(" ") >= 0 ) {
          done( "not a branch tip: " + matches[ 2 ] );
        } else {
          status.branch = matches[ 1 ];
          status.head = matches[ 2 ];
          utils.spawn({
            cmd: "git",
            args: [
              "diff", "--quiet", "HEAD"
            ]
          }, function( err, result, code ) {
            status.changed = code !== 0;
            done( null, status );
          });
        }
      });
    }
  };

  // Load test harness, if there is one
  // A hack, but we can't drop it into tasks/ because loadTasks might evaluate the harness first
  if ( fs.existsSync("./harness") ) {
    helpers.git_status = require("../harness/harness");
  }

  // Compare size to saved sizes
  // Derived and adapted from Corey Frang's original `sizer`
  grunt.registerMultiTask( "compare_size", "Compare working size to saved sizes", function() {
    var done = this.async(),
      newsizes = helpers.sizes( this ),
      cache = helpers.get_cache( sizecache ),
      tips = cache[""].tips,
      labels = helpers.sorted_labels( cache );

    // Obtain the current branch and continue...
    helpers.git_status( function( err, status ) {
      if ( err ) {
        log.warn( err );
        status = {};
      }

      labels.forEach(function( label, index ) {
        var key, diff, color,
            oldsizes = cache[ label ];

        // Skip metadata key
        if ( label === "" ) {
          return;
        }

        // Output header line
        log.write(
          !oldsizes ?
            "Sizes" :
            "Sizes - compared to " +
            ( label[0] === " " ?
              label.slice( 1 ) :
              label )
        );

        if ( label in tips ) {
          log.write( " " + ( "@ " + tips[ label ] )[ "grey" ] );
        }
        log.writeln("");

        // Output size comparisons
        for ( key in newsizes ) {
          diff = oldsizes && oldsizes[ key ] && ( newsizes[ key ] - oldsizes[ key ] );

          if ( diff < 0 ) {
            color = "green";
          } else if ( diff > 0 ) {
            diff = "+" + diff;
            color = "red";
          } else {
            diff = "-";
            color = "grey";
          }

          log.writetableln([ 12, 12, 55 ], [
            utils._.lpad( newsizes[ key ], 10 ) ,
            utils._.lpad( "(" + diff + ")", 10 )[ color ],
            key
          ]);
        }

        // Output blank line for following comparisons
        if ( labels.length > index + 1 ) {
          log.writeln("");
        }
      });

      // Update "last run" sizes
      cache[ lastrun ] = newsizes;

      // Remember if we're at a branch tip and the branch name is an available key
      if ( status.branch && !status.changed && ( status.branch in tips || !cache[ status.branch ] ) ) {
        tips[ status.branch ] = status.head;
        cache[ status.branch ] = newsizes;
        log.writeln( "\nSaved as: " + status.branch );
      }

      // Write to file
      file.write( sizecache, JSON.stringify( cache ) );

      done();
    });

    // Fail task if errors were logged.
    if ( this.errorCount ) {
      return false;
    }
  });

  // List saved sizes
  grunt.registerTask( "compare_size_list", "List saved sizes", function() {
    var cache = helpers.get_cache( sizecache ),
        tips = cache[""].tips;

    helpers.sorted_labels( cache ).forEach(function( label ) {
      // Skip the special labels
      if ( label && label.charAt( 0 ) !== " " ) {
        log.write( label );
        if ( label in tips ) {
          log.write( " " + ( "@ " + tips[ label ] )[ "grey" ] );
        }
        log.writeln("");
      }
    });
  });

  // Add custom label
  grunt.registerTask( "compare_size_add", "Add to saved sizes", function() {
    var label,
        cache = helpers.get_cache( sizecache );

    if ( !cache[ lastrun ] ) {
      log.error("No size data found");
      return false;
    }

    // Store last run sizes under each label, clearing them as branch heads
    for ( label in this.flags ) {
      if ( label in cache[""].tips ) {
        delete cache[""].tips[ label ];
        log.write("(removed branch data) ");
      }
      cache[ label ] = cache[ lastrun ];
      log.writeln( "Last run saved as: " + label );
    }

    file.write( sizecache, JSON.stringify( cache ) );
  });

  // Remove custom label
  grunt.registerTask( "compare_size_remove", "Remove from saved sizes", function() {
    var label,
        cache = helpers.get_cache( sizecache );

    for ( label in this.flags ) {
      delete cache[""].tips[ label ];
      delete cache[ label ];
      log.writeln( "Removed: " + label );
    }

    file.write( sizecache, JSON.stringify( cache ) );
  });

  // Empty size cache
  grunt.registerTask( "compare_size_empty", "Clear all saved sizes", function() {
    if ( fs.existsSync( sizecache ) ) {
      fs.unlinkSync( sizecache );
    }
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-contrib-compress
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 Chris Talkington, contributors
 * Licensed under the MIT license.
 */

'use strict';

var fs = require('fs');
var path = require('path');
var prettySize = require('prettysize');
var zlib = require('zlib');
var archiver = require('archiver');

module.exports = function(grunt) {

  var exports = {
    options: {}
  };

  // Helper for gziping more reliably
  function gzip(engine, buffer, callback) {
    var buffers = [];
    var nread = 0;
    engine.on('error', function(err) {
      callback(err);
    });
    engine.on('data', function(chunk) {
      buffers.push(chunk);
      nread += chunk.length;
    });
    engine.on('end', function() {
      var buf = Buffer.concat(buffers, nread);
      buffers = [];
      callback(null, buf);
    });
    engine.write(buffer);
    engine.end();
  }

  // 1 to 1 gziping of files
  exports.gzip = function(files, done) {
    grunt.util.async.forEachSeries(files, function(file, next) {
      var src = file.src.filter(function(f) {
        return grunt.file.isFile(f);
      }).map(function(f) {
        return grunt.file.read(f);
      }).join('');
      if (src.length < 1) { return next(); }

      // Append ext if the specified one isnt there
      var ext = file.orig.ext || '.gz';
      if (String(file.dest).slice(-ext.length) !== ext) {
        file.dest += ext;
      }

      // Ensure the dest folder exists
      grunt.file.mkdir(path.dirname(file.dest));

      var gz = zlib.createGzip(exports.options);
      gzip(gz, src, function(err, buf) {
        if (err) { return grunt.log.error(err); }
        grunt.file.write(file.dest, buf.toString());
        grunt.log.writeln('File ' + String(file.dest).cyan + ' created (' + exports.getSize(file.dest) + ').');
        next();
      });
    }, done);
  };

  // Compress with tar, tgz and zip
  exports.tar = function(files, done) {
    if (typeof exports.options.archive !== 'string' || exports.options.archive.length === 0) {
      grunt.fail.warn('Unable to compress; no valid archive file was specified.');
    }

    var mode = exports.options.mode;
    var shouldGzip = false;
    if (mode === 'tgz') {
      shouldGzip = true;
      mode = 'tar';
    }

    var archive = archiver.create(mode, exports.options);
    var dest = exports.options.archive;

    // Ensure dest folder exists
    grunt.file.mkdir(path.dirname(dest));

    archive.on('error', function(err) {
      grunt.log.error(err);
      grunt.fail.warn('Archiving failed.');
    });

    // Where to write the file
    archive.pipe(fs.createWriteStream(dest));

    grunt.util.async.forEachSeries(files, function(file, next) {
      var isExpandedPair = file.orig.expand || false;
      var src = file.src.filter(function(f) {
        return grunt.file.isFile(f);
      });

      grunt.util.async.forEachSeries(src, function(srcFile, nextFile) {
        var internalFileName = (isExpandedPair) ? file.dest : exports.unixifyPath(path.join(file.dest || '', srcFile));

        archive.append(fs.createReadStream(srcFile), { name: internalFileName }, function(err) {
          grunt.verbose.writeln('Archiving ' + srcFile.cyan + ' -> ' + String(dest).cyan + '/'.cyan + internalFileName.cyan);
          nextFile(err);
        });
      }, next);
    }, function(err) {
      if (err) { grunt.fail.warn(err); }

      archive.finalize(function(err, written) {
        if (shouldGzip) {
          var gz = zlib.createGzip(exports.options);
          var data = grunt.file.read(dest);
          gzip(gz, data, function(err, buf) {
            grunt.file.write(dest, buf.toString());
            grunt.log.writeln('Created ' + String(dest).cyan + ' (' + exports.getSize(dest) + ')');
            done();
          });
        } else {
          grunt.log.writeln('Created ' + String(dest).cyan + ' (' + exports.getSize(Number(written)) + ')');
          done();
        }
      });
    });
  };

  exports.getSize = function(filename, pretty) {
    var size = 0;
    if (typeof filename === 'string') {
      try {
        size = fs.statSync(filename).size;
      } catch (e) {}
    } else {
      size = filename;
    }
    if (pretty !== false) {
      if (!exports.options.pretty) {
        return size + ' bytes';
      }
      return prettySize(size);
    }
    return Number(size);
  };

  exports.autoDetectMode = function(dest) {
    if (exports.options.mode) {
      return exports.options.mode;
    }
    if (!dest) {
      return 'gzip';
    }
    if (grunt.util._.endsWith(dest, '.tar.gz')) {
      return 'tgz';
    }
    var ext = path.extname(dest).replace('.', '');
    if (ext === 'gz') {
      return 'gzip';
    } else {
      return ext;
    }
  };

  exports.unixifyPath = function(filepath) {
    if (process.platform === 'win32') {
      return filepath.replace(/\\/g, '/');
    } else {
      return filepath;
    }
  };

  return exports;
};<commit_msg>Pipe gzip per @ctalkington suggestion. Fixes GH-29. Fixes GH-32.<commit_after>/*
 * grunt-contrib-compress
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 Chris Talkington, contributors
 * Licensed under the MIT license.
 */

'use strict';

var fs = require('fs');
var path = require('path');
var prettySize = require('prettysize');
var zlib = require('zlib');
var archiver = require('archiver');

module.exports = function(grunt) {

  var exports = {
    options: {}
  };

  // 1 to 1 gziping of files
  exports.gzip = function(files, done) {
    grunt.util.async.forEachSeries(files, function(file, next) {
      var src = file.src.filter(function(f) {
        return grunt.file.isFile(f);
      }).map(function(f) {
        return grunt.file.read(f);
      }).join('');
      if (src.length < 1) { return next(); }

      // Append ext if the specified one isnt there
      var ext = file.orig.ext || '.gz';
      if (String(file.dest).slice(-ext.length) !== ext) {
        file.dest += ext;
      }

      // Ensure the dest folder exists
      grunt.file.mkdir(path.dirname(file.dest));

      var destStream = fs.createWriteStream(file.dest);
      var gzipper = zlib.createGzip(exports.options);

      gzipper.on('error', function(err) {
        grunt.log.error(err);
        grunt.fail.warn('Gziping failed.');
        next();
      });

      destStream.on('close', function() {
        grunt.log.writeln('Created ' + String(file.dest).cyan + ' (' + exports.getSize(file.dest) + ')');
        next();
      });

      gzipper.pipe(destStream);
      gzipper.write(src);
      gzipper.end();
    }, done);
  };

  // Compress with tar, tgz and zip
  exports.tar = function(files, done) {
    if (typeof exports.options.archive !== 'string' || exports.options.archive.length === 0) {
      grunt.fail.warn('Unable to compress; no valid archive file was specified.');
    }

    var mode = exports.options.mode;
    var shouldGzip = false;
    if (mode === 'tgz') {
      shouldGzip = true;
      mode = 'tar';
    }

    var archive = archiver.create(mode, exports.options);
    var dest = exports.options.archive;

    // Ensure dest folder exists
    grunt.file.mkdir(path.dirname(dest));

    archive.on('error', function(err) {
      grunt.log.error(err);
      grunt.fail.warn('Archiving failed.');
    });

    // Where to write the file
    var destStream = fs.createWriteStream(dest);
    if (shouldGzip) {
      var gzipper = zlib.createGzip(exports.options);

      gzipper.on('error', function(err) {
        grunt.log.error(err);
        grunt.fail.warn('Gziping failed.');
      });

      gzipper.on('end', function() {
        grunt.log.writeln('Created ' + String(dest).cyan + ' (' + exports.getSize(dest) + ')');
        done();
      });

      archive.pipe(gzipper).pipe(destStream);
    } else {
      archive.pipe(destStream);
    }

    files.forEach(function(file) {
      var isExpandedPair = file.orig.expand || false;
      var src = file.src.filter(function(f) {
        return grunt.file.isFile(f);
      });

      src.forEach(function(srcFile) {
        var internalFileName = (isExpandedPair) ? file.dest : exports.unixifyPath(path.join(file.dest || '', srcFile));

        archive.append(fs.createReadStream(srcFile), { name: internalFileName }, function(err) {
          grunt.verbose.writeln('Archiving ' + srcFile.cyan + ' -> ' + String(dest).cyan + '/'.cyan + internalFileName.cyan);
        });
      });
    });

    archive.finalize(function(err, written) {
      if (shouldGzip === false) {
        grunt.log.writeln('Created ' + String(dest).cyan + ' (' + exports.getSize(Number(written)) + ')');
        done();
      }
    });
  };

  exports.getSize = function(filename, pretty) {
    var size = 0;
    if (typeof filename === 'string') {
      try {
        size = fs.statSync(filename).size;
      } catch (e) {}
    } else {
      size = filename;
    }
    if (pretty !== false) {
      if (!exports.options.pretty) {
        return size + ' bytes';
      }
      return prettySize(size);
    }
    return Number(size);
  };

  exports.autoDetectMode = function(dest) {
    if (exports.options.mode) {
      return exports.options.mode;
    }
    if (!dest) {
      return 'gzip';
    }
    if (grunt.util._.endsWith(dest, '.tar.gz')) {
      return 'tgz';
    }
    var ext = path.extname(dest).replace('.', '');
    if (ext === 'gz') {
      return 'gzip';
    } else {
      return ext;
    }
  };

  exports.unixifyPath = function(filepath) {
    if (process.platform === 'win32') {
      return filepath.replace(/\\/g, '/');
    } else {
      return filepath;
    }
  };

  return exports;
};<|endoftext|>


<commit_before>/*
 * Increase version number
 *
 * grunt push
 * grunt push:git
 * grunt push:patch
 * grunt push:minor
 * grunt push:major
 *
 * @author Vojta Jina <vojta.jina@gmail.com>
 * @author Mathias Paumgarten <mail@mathias-paumgarten.com>
 * @author Adam Biggs <email@adambig.gs>
 * @author Achim Sperling <achim.sperling@gmail.com>
 */
var semver = require('semver');
var exec = require('child_process').exec;

module.exports = function(grunt) {

  var DESC = 'Increment the version, commit, tag and push.';
  grunt.registerTask('push', DESC, function(versionType, incOrCommitOnly) {
    var opts = this.options({
      bumpVersion: true,
      files: ['package.json'],
      updateConfigs: [], // array of config properties to update (with files)
      releaseBranch: false,
      add: true,
      addFiles: ['.'], // '.' for all files except ingored files in .gitignore
      commit: true,
      commitMessage: 'Release v%VERSION%',
      commitFiles: ['-a'], // '-a' for all files
      createTag: true,
      tagName: 'v%VERSION%',
      tagMessage: 'Version %VERSION%',
      push: true,
      pushTo: 'origin',
      npm: false,
      npmTag: 'Release v%VERSION%',
      gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d'
    });

    if (incOrCommitOnly === 'bump-only') {
      grunt.verbose.writeln('Only incrementing the version.');

      opts.add = false;
      opts.commit = false;
      opts.createTag = false;
      opts.push = false;
    }

    if (incOrCommitOnly === 'commit-only') {
      grunt.verbose.writeln('Only commiting/taggin/pushing.');

      opts.bumpVersion = false;
    }

    if (incOrCommitOnly === 'push-release') {
      grunt.verbose.writeln('Pushing and publishing to NPM.');

      opts.npm = true;
    } else {
      opts.npm = false;
    }

    if (incOrCommitOnly === 'push-publish') {
      grunt.verbose.writeln('Publishing to NPM.');

      opts.bumpVersion = false;
      opts.add = false;
      opts.commit = false;
      opts.createTag = false;
      opts.push = false;
      opts.npm = true;
    }

    var done = this.async();
    var queue = [];
    var next = function() {
      if (!queue.length) {
        return done();
      }
      queue.shift()();
    };
    var runIf = function(condition, behavior) {
      if (condition) {
        queue.push(behavior);
      }
    };

    // MAKE SURE WE'RE ON A RELEASE BRANCH
    runIf(opts.releaseBranch, function() {
      if (opts.npm || opts.commit || opts.push) {
        exec('git rev-parse --abbrev-ref HEAD', function(err, stdout, stderr) {

          if (err || stderr) {
            grunt.fatal('Cannot determine current branch.');
          }

          var currentBranch = stdout.trim();
          var rBranches = (typeof opts.releaseBranch == 'string') ? [opts.releaseBranch] : opts.releaseBranch;

          rBranches.forEach(function(rBranch) {
            if (rBranch == currentBranch) {
              return next();
            }
          });

          grunt.warn('The current branch is not in the list of release branches.');

        });
      }

      next();
    });

    var globalVersion; // when bumping multiple files
    var gitVersion;    // when bumping using `git describe`
    var VERSION_REGEXP = /(\bversion[\'\"]?\s*[:=]\s*[\'\"])([\da-z\.-]+)([\'\"])/i;

    // GET VERSION FROM GIT
    runIf(opts.bumpVersion && versionType === 'git', function(){
      exec('git describe ' + opts.gitDescribeOptions, function(err, stdout, stderr){
        if (err) {
          grunt.fatal('Can not get a version number using `git describe`');
        }
        gitVersion = stdout.trim();
        next();
      });
    });

    // BUMP ALL FILES
    runIf(opts.bumpVersion, function(){
      opts.files.forEach(function(file, idx) {
        var version = null;
        var content = grunt.file.read(file).replace(VERSION_REGEXP, function(match, prefix, parsedVersion, suffix) {
          version = gitVersion || semver.inc(parsedVersion, versionType || 'patch');
          return prefix + version + suffix;
        });

        if (!version) {
          grunt.fatal('Can not find a version to bump in ' + file);
        }

        grunt.file.write(file, content);
        grunt.log.ok('Version bumped to ' + version + (opts.files.length > 1 ? ' (in ' + file + ')' : ''));

        if (!globalVersion) {
          globalVersion = version;
        } else if (globalVersion !== version) {
          grunt.warn('Bumping multiple files with different versions!');
        }

        var configProperty = opts.updateConfigs[idx];
        if (!configProperty) {
          return;
        }

        var cfg = grunt.config(configProperty);
        if (!cfg) {
          return grunt.warn('Can not update "' + configProperty + '" config, it does not exist!');
        }

        cfg.version = version;
        grunt.config(configProperty, cfg);
        grunt.log.ok(configProperty + '\'s version updated');
      });
      next();
    });

    // when only commiting, read the version from package.json / pkg config
    runIf(!opts.bumpVersion, function() {
      if (opts.updateConfigs.length) {
        globalVersion = grunt.config(opts.updateConfigs[0]).version;
      } else {
        globalVersion = grunt.file.readJSON(opts.files[0]).version;
      }

      next();
    });

    // ADD
    runIf(opts.add, function() {
      exec('git add ' + opts.addFiles.join(' '), function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not add files:\n  ' + stderr);
        }
        grunt.log.ok('Added files: "' + opts.addFiles.join(' ') + '"');
        next();
      });
    });

    // COMMIT
    runIf(opts.commit, function() {
      var commitMessage = opts.commitMessage.replace('%VERSION%', globalVersion);

      exec('git commit ' + opts.commitFiles.join(' ') + ' -m "' + commitMessage + '"', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not create the commit:\n  ' + stderr);
        }
        grunt.log.ok('Committed as "' + commitMessage + '"');
        next();
      });
    });

    // CREATE TAG
    runIf(opts.createTag, function() {
      var tagName = opts.tagName.replace('%VERSION%', globalVersion);
      var tagMessage = opts.tagMessage.replace('%VERSION%', globalVersion);

      exec('git tag -a ' + tagName + ' -m "' + tagMessage + '"' , function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not create the tag:\n  ' + stderr);
        }
        grunt.log.ok('Tagged as "' + tagName + '"');
        next();
      });
    });

    // PUSH CHANGES
    runIf(opts.push, function() {
      exec('git push ' + opts.pushTo + ' && git push ' + opts.pushTo + ' --tags', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not push to ' + opts.pushTo + ':\n  ' + stderr);
        }
        grunt.log.ok('Pushed to ' + opts.pushTo);
        next();
      });
    });

    // PUBLISH CHANGES TO NPM
    runIf(opts.npm, function() {
      opts.npmTag.replace('%VERSION%', globalVersion);

      exec('npm publish --tag "' + opts.npmTag + '"', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Publishing to NPM failed:\n  ' + stderr);
        }
        grunt.log.ok('Published to NPM with tag:' + opts.npmTag);
        next();
      });
    });

    next();
  });

  // ALIASES
  DESC = 'Increment the version only.';
  grunt.registerTask('bump-only', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':bump-only');
  });

  DESC = 'Add, commit, tag, push without incrementing the version.';
  grunt.registerTask('push-commit', DESC, 'push::commit-only');

  DESC = 'Bump version, add, commit, tag, push and publish to NPM.';
  grunt.registerTask('push-release', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':push-release');
  });

  DESC = 'Just publish to NPM.';
  grunt.registerTask('push-publish', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':push-publish');
  });
};

<commit_msg>Asynchronous call of next()<commit_after>/*
 * Increase version number
 *
 * grunt push
 * grunt push:git
 * grunt push:patch
 * grunt push:minor
 * grunt push:major
 *
 * @author Vojta Jina <vojta.jina@gmail.com>
 * @author Mathias Paumgarten <mail@mathias-paumgarten.com>
 * @author Adam Biggs <email@adambig.gs>
 * @author Achim Sperling <achim.sperling@gmail.com>
 */
var semver = require('semver');
var exec = require('child_process').exec;

module.exports = function(grunt) {

  var DESC = 'Increment the version, commit, tag and push.';
  grunt.registerTask('push', DESC, function(versionType, incOrCommitOnly) {
    var opts = this.options({
      bumpVersion: true,
      files: ['package.json'],
      updateConfigs: [], // array of config properties to update (with files)
      releaseBranch: false,
      add: true,
      addFiles: ['.'], // '.' for all files except ingored files in .gitignore
      commit: true,
      commitMessage: 'Release v%VERSION%',
      commitFiles: ['-a'], // '-a' for all files
      createTag: true,
      tagName: 'v%VERSION%',
      tagMessage: 'Version %VERSION%',
      push: true,
      pushTo: 'origin',
      npm: false,
      npmTag: 'Release v%VERSION%',
      gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d'
    });

    if (incOrCommitOnly === 'bump-only') {
      grunt.verbose.writeln('Only incrementing the version.');

      opts.add = false;
      opts.commit = false;
      opts.createTag = false;
      opts.push = false;
    }

    if (incOrCommitOnly === 'commit-only') {
      grunt.verbose.writeln('Only commiting/taggin/pushing.');

      opts.bumpVersion = false;
    }

    if (incOrCommitOnly === 'push-release') {
      grunt.verbose.writeln('Pushing and publishing to NPM.');

      opts.npm = true;
    } else {
      opts.npm = false;
    }

    if (incOrCommitOnly === 'push-publish') {
      grunt.verbose.writeln('Publishing to NPM.');

      opts.bumpVersion = false;
      opts.add = false;
      opts.commit = false;
      opts.createTag = false;
      opts.push = false;
      opts.npm = true;
    }

    var done = this.async();
    var queue = [];
    var next = function() {
      if (!queue.length) {
        return done();
      }
      queue.shift()();
    };
    var runIf = function(condition, behavior) {
      if (condition) {
        queue.push(behavior);
      }
    };

    // MAKE SURE WE'RE ON A RELEASE BRANCH
    runIf(opts.releaseBranch && (opts.npm || opts.commit || opts.push), function() {
      exec('git rev-parse --abbrev-ref HEAD', function(err, stdout, stderr) {

        if (err || stderr) {
          grunt.fatal('Cannot determine current branch.');
        }

        var currentBranch = stdout.trim();
        var rBranches = (typeof opts.releaseBranch == 'string') ? [opts.releaseBranch] : opts.releaseBranch;

        for (var i = rBranches.length - 1; i >= 0; i--) {
          console.log('<'+rBranches[i] + "> - <" + currentBranch+'>');
          if (rBranches[i] == currentBranch) {
            return next();
          }
        }

        grunt.warn('The current branch is not in the list of release branches.');

        // Allow for --force
        next();

      });
    });

    var globalVersion; // when bumping multiple files
    var gitVersion;    // when bumping using `git describe`
    var VERSION_REGEXP = /(\bversion[\'\"]?\s*[:=]\s*[\'\"])([\da-z\.-]+)([\'\"])/i;

    // GET VERSION FROM GIT
    runIf(opts.bumpVersion && versionType === 'git', function(){
      exec('git describe ' + opts.gitDescribeOptions, function(err, stdout, stderr){
        if (err) {
          grunt.fatal('Can not get a version number using `git describe`');
        }
        gitVersion = stdout.trim();
        next();
      });
    });

    // BUMP ALL FILES
    runIf(opts.bumpVersion, function(){
      opts.files.forEach(function(file, idx) {
        var version = null;
        var content = grunt.file.read(file).replace(VERSION_REGEXP, function(match, prefix, parsedVersion, suffix) {
          version = gitVersion || semver.inc(parsedVersion, versionType || 'patch');
          return prefix + version + suffix;
        });

        if (!version) {
          grunt.fatal('Can not find a version to bump in ' + file);
        }

        grunt.file.write(file, content);
        grunt.log.ok('Version bumped to ' + version + (opts.files.length > 1 ? ' (in ' + file + ')' : ''));

        if (!globalVersion) {
          globalVersion = version;
        } else if (globalVersion !== version) {
          grunt.warn('Bumping multiple files with different versions!');
        }

        var configProperty = opts.updateConfigs[idx];
        if (!configProperty) {
          return;
        }

        var cfg = grunt.config(configProperty);
        if (!cfg) {
          return grunt.warn('Can not update "' + configProperty + '" config, it does not exist!');
        }

        cfg.version = version;
        grunt.config(configProperty, cfg);
        grunt.log.ok(configProperty + '\'s version updated');
      });
      next();
    });

    // when only commiting, read the version from package.json / pkg config
    runIf(!opts.bumpVersion, function() {
      if (opts.updateConfigs.length) {
        globalVersion = grunt.config(opts.updateConfigs[0]).version;
      } else {
        globalVersion = grunt.file.readJSON(opts.files[0]).version;
      }

      next();
    });

    // ADD
    runIf(opts.add, function() {
      exec('git add ' + opts.addFiles.join(' '), function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not add files:\n  ' + stderr);
        }
        grunt.log.ok('Added files: "' + opts.addFiles.join(' ') + '"');
        next();
      });
    });

    // COMMIT
    runIf(opts.commit, function() {
      var commitMessage = opts.commitMessage.replace('%VERSION%', globalVersion);

      exec('git commit ' + opts.commitFiles.join(' ') + ' -m "' + commitMessage + '"', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not create the commit:\n  ' + stderr);
        }
        grunt.log.ok('Committed as "' + commitMessage + '"');
        next();
      });
    });

    // CREATE TAG
    runIf(opts.createTag, function() {
      var tagName = opts.tagName.replace('%VERSION%', globalVersion);
      var tagMessage = opts.tagMessage.replace('%VERSION%', globalVersion);

      exec('git tag -a ' + tagName + ' -m "' + tagMessage + '"' , function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not create the tag:\n  ' + stderr);
        }
        grunt.log.ok('Tagged as "' + tagName + '"');
        next();
      });
    });

    // PUSH CHANGES
    runIf(opts.push, function() {
      exec('git push ' + opts.pushTo + ' && git push ' + opts.pushTo + ' --tags', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Can not push to ' + opts.pushTo + ':\n  ' + stderr);
        }
        grunt.log.ok('Pushed to ' + opts.pushTo);
        next();
      });
    });

    // PUBLISH CHANGES TO NPM
    runIf(opts.npm, function() {
      opts.npmTag.replace('%VERSION%', globalVersion);

      exec('npm publish --tag "' + opts.npmTag + '"', function(err, stdout, stderr) {
        if (err) {
          grunt.fatal('Publishing to NPM failed:\n  ' + stderr);
        }
        grunt.log.ok('Published to NPM with tag:' + opts.npmTag);
        next();
      });
    });

    next();
  });

  // ALIASES
  DESC = 'Increment the version only.';
  grunt.registerTask('bump-only', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':bump-only');
  });

  DESC = 'Add, commit, tag, push without incrementing the version.';
  grunt.registerTask('push-commit', DESC, 'push::commit-only');

  DESC = 'Bump version, add, commit, tag, push and publish to NPM.';
  grunt.registerTask('push-release', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':push-release');
  });

  DESC = 'Just publish to NPM.';
  grunt.registerTask('push-publish', DESC, function(versionType) {
    grunt.task.run('push:' + (versionType || '') + ':push-publish');
  });
};

<|endoftext|>


<commit_before>/*
 * grunt-sauce-tunnel
 * https://github.com/civitaslearning/grunt-sauce-tunnel
 *
 * Copyright (c) 2013 Dan Harbin
 * Licensed under the MIT license.
 */

'use strict';

(function () {
	var SauceTunnel = require('sauce-tunnel'),
        tunnels =  {};

	module.exports = function (grunt) {
		function configureLogEvents(tunnel) {
			var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
			methods.forEach(function (method) {
				tunnel.on('log:' + method, function (text) {
					grunt.log[method](text);
				});
				tunnel.on('verbose:' + method, function (text) {
					grunt.verbose[method](text);
				});
			});
		}

		grunt.registerMultiTask('sauce_tunnel_stop', 'Stop the Sauce Labs tunnel', function () {
			// Merge task-specific and/or target-specific options with these defaults.
			var options = this.options({
				username: process.env.SAUCE_USERNAME,
				key: process.env.SAUCE_ACCESS_KEY
			});

            var done = null,
                tunnel = null;

            // try to find active tunnel
            tunnel = tunnels[options.identifier];

			if(!tunnel){
				tunnel = new SauceTunnel(
					options.username,
					options.key,
					options.identifier,
					false, // tunneled = true
					['-v']
				);
			}

            done = grunt.task.current.async();

            var finished = function(err){
                if(err){
                    grunt.fail.warn(err);
                }
                if (done) {
                    done();
                    done = null;
                }

            };

			tunnel.stop(finished);

		});

		grunt.registerMultiTask('sauce_tunnel', 'Runs the Sauce Labs tunnel', function () {
			// Merge task-specific and/or target-specific options with these defaults.
			var options = this.options({
				username: process.env.SAUCE_USERNAME,
				key: process.env.SAUCE_ACCESS_KEY
			});

			var done = null,
				tunnel = null;

			var finished = function () {
				if (done) {
					done();
					done = null;
				}
			};

			function start(options) {
				if (tunnel) {
					stop();

					if (grunt.task.current.flags.stop) {
						finished();
						return;
					}
				}

				done = grunt.task.current.async();

				tunnel = new SauceTunnel(
					options.username,
					options.key,
					options.identifier,
					true, // tunneled = true
					['-v']
				);

                // keep actives tunnel in memory for stop task
                tunnels[tunnel.identifier] = tunnel;

				configureLogEvents(tunnel);

				grunt.log.writeln('Open'.cyan + ' Sauce Labs tunnel: ' + tunnel.identifier.cyan);

				tunnel.start(function (status) {
					if (status === false) {
						grunt.fatal('Failed'.red + ' to open Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					}

					grunt.log.ok('Successfully'.green + ' opened Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					finished();
				});

				tunnel.on('exit', finished);
				tunnel.on('exit', stop);
			}

			function stop() {
				if (tunnel && tunnel.stop) {
					grunt.log.writeln('Stopping'.cyan + 'Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					tunnel.stop(function () {
						grunt.log.writeln('Stopped'.red + 'Sauce Labs tunnel: ' + tunnel.identifier.cyan);
						tunnel = null;
						finished();
					});
				} else {
					finished();
				}
			}

			start(options);
		});
	};
})();<commit_msg>correct indentation<commit_after>/*
 * grunt-sauce-tunnel
 * https://github.com/civitaslearning/grunt-sauce-tunnel
 *
 * Copyright (c) 2013 Dan Harbin
 * Licensed under the MIT license.
 */

'use strict';

(function () {
	var SauceTunnel = require('sauce-tunnel'),
	tunnels =  {};

	module.exports = function (grunt) {
		function configureLogEvents(tunnel) {
			var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
			methods.forEach(function (method) {
				tunnel.on('log:' + method, function (text) {
					grunt.log[method](text);
				});
				tunnel.on('verbose:' + method, function (text) {
					grunt.verbose[method](text);
				});
			});
		}

		grunt.registerMultiTask('sauce_tunnel_stop', 'Stop the Sauce Labs tunnel', function () {
			// Merge task-specific and/or target-specific options with these defaults.
			var options = this.options({
				username: process.env.SAUCE_USERNAME,
				key: process.env.SAUCE_ACCESS_KEY
			});

			var done = null,
			tunnel = null;

			// try to find active tunnel
			tunnel = tunnels[options.identifier];

			if(!tunnel){
				tunnel = new SauceTunnel(
					options.username,
					options.key,
					options.identifier,
					false, // tunneled = true
					['-v']
				);
			}

			done = grunt.task.current.async();

			var finished = function(err){
				if(err){
					grunt.fail.warn(err);
				}
				if (done) {
					done();
					done = null;
				}

			};

			tunnel.stop(finished);

		});

		grunt.registerMultiTask('sauce_tunnel', 'Runs the Sauce Labs tunnel', function () {
			// Merge task-specific and/or target-specific options with these defaults.
			var options = this.options({
				username: process.env.SAUCE_USERNAME,
				key: process.env.SAUCE_ACCESS_KEY
			});

			var done = null,
				tunnel = null;

			var finished = function () {
				if (done) {
					done();
					done = null;
				}
			};

			function start(options) {
				if (tunnel) {
					stop();

					if (grunt.task.current.flags.stop) {
						finished();
						return;
					}
				}

				done = grunt.task.current.async();

				tunnel = new SauceTunnel(
					options.username,
					options.key,
					options.identifier,
					true, // tunneled = true
					['-v']
				);

				// keep actives tunnel in memory for stop task
				tunnels[tunnel.identifier] = tunnel;

				configureLogEvents(tunnel);

				grunt.log.writeln('Open'.cyan + ' Sauce Labs tunnel: ' + tunnel.identifier.cyan);

				tunnel.start(function (status) {
					if (status === false) {
						grunt.fatal('Failed'.red + ' to open Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					}

					grunt.log.ok('Successfully'.green + ' opened Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					finished();
				});

				tunnel.on('exit', finished);
				tunnel.on('exit', stop);
			}

			function stop() {
				if (tunnel && tunnel.stop) {
					grunt.log.writeln('Stopping'.cyan + 'Sauce Labs tunnel: ' + tunnel.identifier.cyan);
					tunnel.stop(function () {
						grunt.log.writeln('Stopped'.red + 'Sauce Labs tunnel: ' + tunnel.identifier.cyan);
						tunnel = null;
						finished();
					});
				} else {
					finished();
				}
			}

			start(options);
		});
	};
})();<|endoftext|>


<commit_before>/**
 * grunt-uncss-inline
 * https://github.com/sparanoid/grunt-uncss-inline
 *
 * Copyright (c) 2016 Tunghsiao Liu
 * Licensed under the MIT license.
 */
/**
 * grunt-uncss
 * https://github.com/addyosmani/grunt-uncss
 *
 * Copyright (c) 2016 Addy Osmani
 * Licensed under the MIT license.
 */

module.exports = function ( grunt ) {

  'use strict';

  var uncss   = require( 'uncss' ),
      chalk   = require( 'chalk' ),
      maxmin  = require( 'maxmin' ),
      async   = require( 'async' ),
      cheerio = require( 'cheerio' );

  grunt.registerMultiTask( 'uncss_inline', 'Remove unused CSS', function () {

    var done = this.async();
    var options = this.options({
      report: 'min'
    });

    function processFile ( file, done ) {

      // Get stylesheets from inline <style>
      var $ = cheerio.load(grunt.file.read(file.src), {
        decodeEntities: false
      });

      var src = file.src.filter(function ( filepath ) {
        // Warn on and remove invalid source files (if nonull was set).
        if ( !grunt.file.exists( filepath ) ) {
          grunt.log.warn( 'Source file ' + chalk.cyan( filepath ) + ' not found.' );
          return false;
        } else {
          return true;
        }
      });

      if ( src.length === 0 && file.src.length === 0 ) {
        grunt.fail.warn( 'Destination (' + file.dest + ') not written because src files were empty.' );
      }

      var styles = [];
      $('style').each(function () {
        var style = $(this).html();
        if (style) {
          styles.push(style);
          options.raw = styles.join(' ');
        }
      });

      try {
        uncss( src, options, function ( error, output, report ) {
          if ( error ) {
            throw error;
          }

          // remove all `<style>` tags except the last one
          var styleTags = $('style');
          styleTags.slice(0, styleTags.length - 1).remove();
          $('style').text(output);
          var html = $.html();
          grunt.file.write( file.dest, html );

          grunt.log.writeln('File ' + chalk.cyan( file.dest ) + ' created: ' + maxmin( report.original, output, options.report === 'gzip' ) );
          if (typeof(options.reportFile) !== 'undefined' && options.reportFile.length > 0) {
            grunt.file.write(options.reportFile, JSON.stringify(report));
          }
          done();
        });
      } catch ( e ) {
        var err = new Error( 'Uncss failed.' );
        if ( e.msg ) {
          err.message += ', ' + e.msg + '.';
        }
        err.origError = e;
        grunt.log.warn( 'Uncssing source "' + src + '" failed.' );
        grunt.fail.warn( err );
      }

    }

    if (this.files.length === 1) {
      processFile( this.files[0], done );
    } else {
      // Processing multiple files must be done sequentially
      // until https://github.com/giakki/uncss/issues/136 is resolved.
      async.eachSeries( this.files, processFile, done );
    }

  });

};
<commit_msg>feat: preserve the first  for final process<commit_after>/**
 * grunt-uncss-inline
 * https://github.com/sparanoid/grunt-uncss-inline
 *
 * Copyright (c) 2016 Tunghsiao Liu
 * Licensed under the MIT license.
 */
/**
 * grunt-uncss
 * https://github.com/addyosmani/grunt-uncss
 *
 * Copyright (c) 2016 Addy Osmani
 * Licensed under the MIT license.
 */

module.exports = function ( grunt ) {

  'use strict';

  var uncss   = require( 'uncss' ),
      chalk   = require( 'chalk' ),
      maxmin  = require( 'maxmin' ),
      async   = require( 'async' ),
      cheerio = require( 'cheerio' );

  grunt.registerMultiTask( 'uncss_inline', 'Remove unused CSS', function () {

    var done = this.async();
    var options = this.options({
      report: 'min'
    });

    function processFile ( file, done ) {

      // Get stylesheets from inline <style>
      var $ = cheerio.load(grunt.file.read(file.src), {
        decodeEntities: false
      });

      var src = file.src.filter(function ( filepath ) {
        // Warn on and remove invalid source files (if nonull was set).
        if ( !grunt.file.exists( filepath ) ) {
          grunt.log.warn( 'Source file ' + chalk.cyan( filepath ) + ' not found.' );
          return false;
        } else {
          return true;
        }
      });

      if ( src.length === 0 && file.src.length === 0 ) {
        grunt.fail.warn( 'Destination (' + file.dest + ') not written because src files were empty.' );
      }

      var styles = [];
      $('style').each(function () {
        var style = $(this).html();
        if (style) {
          styles.push(style);
          options.raw = styles.join(' ');
        }
      });

      try {
        uncss( src, options, function ( error, output, report ) {
          if ( error ) {
            throw error;
          }

          // remove all `<style>` tags except the first one
          $('style').slice(1).remove();
          $('style').text(output);
          var html = $.html();
          grunt.file.write( file.dest, html );

          grunt.log.writeln('File ' + chalk.cyan( file.dest ) + ' created: ' + maxmin( report.original, output, options.report === 'gzip' ) );
          if (typeof(options.reportFile) !== 'undefined' && options.reportFile.length > 0) {
            grunt.file.write(options.reportFile, JSON.stringify(report));
          }
          done();
        });
      } catch ( e ) {
        var err = new Error( 'Uncss failed.' );
        if ( e.msg ) {
          err.message += ', ' + e.msg + '.';
        }
        err.origError = e;
        grunt.log.warn( 'Uncssing source "' + src + '" failed.' );
        grunt.fail.warn( err );
      }

    }

    if (this.files.length === 1) {
      processFile( this.files[0], done );
    } else {
      // Processing multiple files must be done sequentially
      // until https://github.com/giakki/uncss/issues/136 is resolved.
      async.eachSeries( this.files, processFile, done );
    }

  });

};
<|endoftext|>


<commit_before>/**
 * Task to slice and conver video file into scenes required for correct custom video player
 * operation.
 *
 * This task require ffmpeg to be installed
 * Install ffmpeg with brew:
 * brew install ffmpeg --with-libvorbis --with-nonfree --with-gpl --with-libvpx --with-pthreads --with-libx264 --with-libfaac --with-theora --with-libogg
 *
 * Examples:
 * 	video_slicer: {
 * 		options: {
 * 			sections: [
 * 				{
 * 					name: 'section0-sequence',
 * 					time: [1, 2],
 * 					json: true
 * 				}, {
 * 					name: 'section0',
 * 					time: [1, 17],
 * 					codecs: ['mp4', 'webm']
 * 				}, {
 * 					name: 'section1',
 * 					time: [19, 30],
 * 					codecs: 'mp4'
 * 				}, {
 * 					name: 'section2',
 * 					time: 56
 * 				}
 * 			]
 * 		},
 * 		video: {
 * 			src: 'videos/video.mp4',
 * 			dest: 'sections/'
 * 		}
 * }
 */

var fs = require('fs'),
	util = require('util'),
	mime = require('mime'),
	async = require('async'),
	_ = require('underscore'),
	ffmpeg = require('ffmpeg-node');

module.exports = function(grunt) {
	var defaults = {
			sections: [{}],
			emptyDestBeforeStart: false,
			encodes: {
				webm: {
					'-c:v': 'libvpx',
					'-qmin': '0',
					'-qmax': '50',
					'-crf': '10',
					'-b:v': '2M',
					'-c:a': 'libvorbis',
					'-q:a': '4',
					'-threads': '0'
				},
				mp4: {
					'-strict': 'experimental',
					'-f': 'mp4',
					'-movflags': 'faststart',
					'-vcodec': 'libx264',
					'-acodec': 'aac',
					'-ab': '160000',
					'-ac': '2',
					'-preset': 'slow',
					'-profile': 'main',
					'-crf': '22',
					'-threads': '0'
				},
				json: {
					'-an': '',
					'-f': 'image2'
				}
			}
		};

	grunt.registerMultiTask('video_slicer', 'Slice videos as you want', function() {
		var options = this.options(defaults),
			files = this.files,
			done = this.async(),
			deletedDest = {},
			queue = [],
			processingCount = 0;

		function addToQueue(encodeFlags, params) {
			var ffmpegParams = ['-i', params.srcPath];

			_.each(encodeFlags, function(value, key, list) {
				ffmpegParams.push(key);

				switch(typeof value) {
					case 'string':
						if (value) {
							ffmpegParams.push(value);
						}
						break;
					default:
						ffmpegParams.push(value);
				}
			});
			ffmpegParams.push(params.outhPath);

			if (!grunt.file.isDir(params.dstPath)) {
				grunt.file.mkdir(params.dstPath);
			}

			queue.push(function(callback) {
				var timer = setInterval(function() {
						grunt.log.write('.');
					}, 1000);

				grunt.verbose.writeln('ffmpeg ' + ffmpegParams.join(' '));
				grunt.log.write('Converting: ' + (params.log || params.outhPath));

				ffmpeg.exec(ffmpegParams, function() {
					if (typeof params.preprocess === 'function') {
						params.preprocess(params);
					}
					timer && clearInterval(timer);
					grunt.log.write('.' + 'OK'.yellow + ' (' + (++processingCount) + '/' + queue.length + ')');
					grunt.log.writeln('');
					return callback();
				});
			});
		}

		function executeQueue() {
			grunt.log.writeln('Starting ' + queue.length.toString().cyan + ' encoding jobs.');
			async.series(queue, done);
		}

		if (!_.isArray(options.sections) || !options.sections.length) {
			return grunt.fail.warn('No sections have been defined.');
		}

		this.files.forEach(function(f) {
			options.sections.forEach(function(section, i) {
				var section = _.defaults(section, {
						name: 'section' + i,
						time: [],
						codecs: ['mp4', 'webm'],
						skip: false
					}),
					srcPath = f.src[0],
					dstPath = f.dest,
					hasTimeRange;

				if (section.skip) {
					return grunt.log.writeln('Skipping: ' + section.name);
				}

				if (_.isArray(section.time)) {
					switch(section.time.length) {
						case 2:
						case 1:
							hasTimeRange = true;
							break;
						case 0:
							hasTimeRange = false;
							break;
						default:
							section.time.length = 2;
							hasTimeRange = true;
					}
				} else {
					section.time = [section.time];
					hasTimeRange = true;
				}

				if (!grunt.file.exists(srcPath)) {
					return grunt.fail.warn('Source file "' + srcPath + '" not found.');
				}

				if (options.emptyDestBeforeStart && grunt.file.isDir(dstPath) && !deletedDest[dstPath]) {
					deletedDest[dstPath] = true;
					grunt.file.delete(dstPath);
				}

				if (_.isString(section.codecs)) {
					section.codecs = [section.codecs];
				}

				if (_.isArray(section.codecs)) {
					section.codecs.forEach(function(codecName) {
						var defaultEncodeFlags = defaults.encodes[codecName],
							encodeFlags = _.extend({}, defaultEncodeFlags),
							encodeParams = {
								name: section.name,
								srcPath: srcPath,
								dstPath: dstPath,
								outhPath: null,
								log: null
							},
							outPath;

						if (defaultEncodeFlags) {
							switch(codecName) {
								case 'json':
									outPath = dstPath + section.name;

									_.extend(encodeParams, {
										dstPath: outPath,
										sequencePath: dstPath,
										outhPath: outPath + '/%d.jpg',
										log: srcPath + '#t=' + section.time.join(',') + ' -> ' + outPath + '.json',
										preprocess: function(params) {
											var result = {
													frames: []
												};

											grunt.file.recurse(params.sequencePath, function(abspath, rootdir, subdir, filename) {
												result.frames[filename.split('.')[0] - 1] = util.format('data:%s;base64,%s', mime.lookup(abspath), fs.readFileSync(abspath).toString('base64'));
											});
											grunt.file.write(params.sequencePath + params.name + '.json', JSON.stringify(result));
										}
									});

									if (grunt.file.isDir(outPath)) {
										grunt.file.delete(outPath);
									}
									break;
								default:
									outPath = dstPath + section.name + '.' + codecName;

									_.extend(encodeParams, {
										outhPath: outPath,
										log: srcPath + (hasTimeRange ? '#t=' + section.time.join(',') : '') + ' -> ' + outPath
									});

									if (grunt.file.exists(outPath)) {
										grunt.file.delete(outPath);
									}
							}

							// Задаем время секции для нарезки.
							if (hasTimeRange) {
								encodeFlags['-ss'] = section.time[0];

								if (section.time[1] != null) {
									encodeFlags['-t'] = section.time[1] - section.time[0];
								}
							}

							// Добавляем задачу в очередь.
							addToQueue(encodeFlags, encodeParams);
						}
					});
				} else {
					return grunt.fail.warn('No codecs defined for section "' + section.name + '"');
				}
			});
		});
		executeQueue();
	});
};<commit_msg>grunt log colorizing<commit_after>/**
 * Task to slice and conver video file into scenes required for correct custom video player
 * operation.
 *
 * This task require ffmpeg to be installed
 * Install ffmpeg with brew:
 * brew install ffmpeg --with-libvorbis --with-nonfree --with-gpl --with-libvpx --with-pthreads --with-libx264 --with-libfaac --with-theora --with-libogg
 *
 * Examples:
 * 	video_slicer: {
 * 		options: {
 * 			sections: [
 * 				{
 * 					name: 'section0-sequence',
 * 					time: [1, 2],
 * 					json: true
 * 				}, {
 * 					name: 'section0',
 * 					time: [1, 17],
 * 					codecs: ['mp4', 'webm']
 * 				}, {
 * 					name: 'section1',
 * 					time: [19, 30],
 * 					codecs: 'mp4'
 * 				}, {
 * 					name: 'section2',
 * 					time: 56
 * 				}
 * 			]
 * 		},
 * 		video: {
 * 			src: 'videos/video.mp4',
 * 			dest: 'sections/'
 * 		}
 * }
 */

var fs = require('fs'),
	util = require('util'),
	mime = require('mime'),
	async = require('async'),
	_ = require('underscore'),
	ffmpeg = require('ffmpeg-node');

module.exports = function(grunt) {
	var defaults = {
			sections: [{}],
			emptyDestBeforeStart: false,
			encodes: {
				webm: {
					'-c:v': 'libvpx',
					'-qmin': '0',
					'-qmax': '50',
					'-crf': '10',
					'-b:v': '2M',
					'-c:a': 'libvorbis',
					'-q:a': '4',
					'-threads': '0'
				},
				mp4: {
					'-strict': 'experimental',
					'-f': 'mp4',
					'-movflags': 'faststart',
					'-vcodec': 'libx264',
					'-acodec': 'aac',
					'-ab': '160000',
					'-ac': '2',
					'-preset': 'slow',
					'-profile': 'main',
					'-crf': '22',
					'-threads': '0'
				},
				json: {
					'-an': '',
					'-f': 'image2'
				}
			}
		};

	grunt.registerMultiTask('video_slicer', 'Slice videos as you want', function() {
		var options = this.options(defaults),
			files = this.files,
			done = this.async(),
			deletedDest = {},
			queue = [],
			processingCount = 0;

		function addToQueue(encodeFlags, params) {
			var ffmpegParams = ['-i', params.srcPath];

			_.each(encodeFlags, function(value, key, list) {
				ffmpegParams.push(key);

				switch(typeof value) {
					case 'string':
						if (value) {
							ffmpegParams.push(value);
						}
						break;
					default:
						ffmpegParams.push(value);
				}
			});
			ffmpegParams.push(params.outhPath);

			if (!grunt.file.isDir(params.dstPath)) {
				grunt.file.mkdir(params.dstPath);
			}

			queue.push(function(callback) {
				var timer = setInterval(function() {
						grunt.log.write('.');
					}, 1000);

				grunt.verbose.writeln('ffmpeg ' + ffmpegParams.join(' '));
				grunt.log.write('Converting: ' + (params.log || params.outhPath).cyan);

				ffmpeg.exec(ffmpegParams, function() {
					if (typeof params.preprocess === 'function') {
						params.preprocess(params);
					}
					timer && clearInterval(timer);
					grunt.log.write('.' + 'OK'.yellow + ' (' + (++processingCount) + '/' + queue.length + ')');
					grunt.log.writeln('');
					return callback();
				});
			});
		}

		function executeQueue() {
			grunt.log.writeln('Starting ' + queue.length.toString().cyan + ' encoding jobs.');
			async.series(queue, done);
		}

		if (!_.isArray(options.sections) || !options.sections.length) {
			return grunt.fail.warn('No sections have been defined.');
		}

		this.files.forEach(function(f) {
			options.sections.forEach(function(section, i) {
				var section = _.defaults(section, {
						name: 'section' + i,
						time: [],
						codecs: ['mp4', 'webm'],
						skip: false
					}),
					srcPath = f.src[0],
					dstPath = f.dest,
					hasTimeRange;

				if (section.skip) {
					return grunt.log.writeln('Skipping: ' + section.name);
				}

				if (_.isArray(section.time)) {
					switch(section.time.length) {
						case 2:
						case 1:
							hasTimeRange = true;
							break;
						case 0:
							hasTimeRange = false;
							break;
						default:
							section.time.length = 2;
							hasTimeRange = true;
					}
				} else {
					section.time = [section.time];
					hasTimeRange = true;
				}

				if (!grunt.file.exists(srcPath)) {
					return grunt.fail.warn('Source file "' + srcPath + '" not found.');
				}

				if (options.emptyDestBeforeStart && grunt.file.isDir(dstPath) && !deletedDest[dstPath]) {
					deletedDest[dstPath] = true;
					grunt.file.delete(dstPath);
				}

				if (_.isString(section.codecs)) {
					section.codecs = [section.codecs];
				}

				if (_.isArray(section.codecs)) {
					section.codecs.forEach(function(codecName) {
						var defaultEncodeFlags = defaults.encodes[codecName],
							encodeFlags = _.extend({}, defaultEncodeFlags),
							encodeParams = {
								name: section.name,
								srcPath: srcPath,
								dstPath: dstPath,
								outhPath: null,
								log: null
							},
							outPath;

						if (defaultEncodeFlags) {
							switch(codecName) {
								case 'json':
									outPath = dstPath + section.name;

									_.extend(encodeParams, {
										dstPath: outPath,
										sequencePath: dstPath,
										outhPath: outPath + '/%d.jpg',
										log: srcPath + '#t=' + section.time.join(',') + ' -> ' + outPath + '.json',
										preprocess: function(params) {
											var result = {
													frames: []
												};

											grunt.file.recurse(params.sequencePath, function(abspath, rootdir, subdir, filename) {
												result.frames[filename.split('.')[0] - 1] = util.format('data:%s;base64,%s', mime.lookup(abspath), fs.readFileSync(abspath).toString('base64'));
											});
											grunt.file.write(params.sequencePath + params.name + '.json', JSON.stringify(result));
										}
									});

									if (grunt.file.isDir(outPath)) {
										grunt.file.delete(outPath);
									}
									break;
								default:
									outPath = dstPath + section.name + '.' + codecName;

									_.extend(encodeParams, {
										outhPath: outPath,
										log: srcPath + (hasTimeRange ? '#t=' + section.time.join(',') : '') + ' -> ' + outPath
									});

									if (grunt.file.exists(outPath)) {
										grunt.file.delete(outPath);
									}
							}

							// Задаем время секции для нарезки.
							if (hasTimeRange) {
								encodeFlags['-ss'] = section.time[0];

								if (section.time[1] != null) {
									encodeFlags['-t'] = section.time[1] - section.time[0];
								}
							}

							// Добавляем задачу в очередь.
							addToQueue(encodeFlags, encodeParams);
						}
					});
				} else {
					return grunt.fail.warn('No codecs defined for section "' + section.name + '"');
				}
			});
		});
		executeQueue();
	});
};<|endoftext|>


<commit_before>var fs = require('fs');

var routes = function(api, next){

  api.routes = {};
  api.routes.routes = {};
  api.routes.routesFile = api.project_root + '/routes.js';

  ////////////////////////////////////////////////////////////////////////////
  // route processing for web clients
  api.routes.processRoute = function(connection){
    if(connection.params['action'] == null || typeof api.actions.actions[connection.params['action']] === 'undefined'){
      var method = connection.rawConnection.method.toLowerCase();
      for(var i in api.routes.routes[method]){
        var route = api.routes.routes[method][i];
        var match = api.routes.matchURL(connection.rawConnection.parsedURL.pathname, route.path);
        if(match.match === true){
          for(var param in match.params){
            var decodedName = decodeURIComponent(param.replace(/\+/g, ' '));
            var decodedValue = decodeURIComponent(match.params[param].replace(/\+/g, ' '));
            connection.params[decodedName] = decodedValue;
          }
          connection.params['action'] = route.action;
          break;
        }
      }
    }
  }

  api.routes.matchURL = function(url, match){
    var response = {match: false, params: {} }
    var urlParts = url.split('/');
    var matchParts = match.split('/');
    var regexp = '';
    var variable = '';
    if(urlParts[0] == ''){ urlParts.splice(0, 1) }
    if(matchParts[0] == ''){ matchParts.splice(0, 1) }
    if(urlParts[(urlParts.length - 1)] == ''){ urlParts.pop() }
    if(matchParts[(matchParts.length - 1)] == ''){ matchParts.pop() }
    if(urlParts[0] == api.config.servers.web.urlPathForActions){ urlParts.splice(0, 1) }
    for(var i in matchParts){
      var part = matchParts[i];
      if(!urlParts[i]){
        return response;
      } else if(part[0] === ':' && part.indexOf('(') < 0){
        variable = part.replace(':', '');
        response.params[variable] = urlParts[i];
      } else if(part[0] === ':' && part.indexOf('(') >= 0){
        variable = part.replace(':', '').split('(')[0];
        regexp = part.split('(')[1];
        var matches = urlParts[i].match(new RegExp(regexp.substring(0, regexp.length - 1), 'g'));
        if(matches != null){
          response.params[variable] = urlParts[i];
        } else {
          return response;
        }
      } else {
        if(urlParts[i] == null || urlParts[i].toLowerCase() != matchParts[i].toLowerCase()){
          return response;
        }
      }
    }
    response.match = true;
    return response;
  }

  // load in the routes file
  api.routes.loadRoutes = function(rawRoutes){
    var counter = 0;
    api.routes.routes = { 'get': [], 'post': [], 'put': [], 'patch' : [], 'delete': [] };

    if(rawRoutes == null){
      if(fs.existsSync(api.routes.routesFile)){
        delete require.cache[require.resolve(api.routes.routesFile)];
        rawRoutes = require(api.routes.routesFile).routes;
      } else {
        api.log('no routes file found, skipping', 'debug');
        return;
      }
    }
    for(var i in rawRoutes){
      var method = i.toLowerCase();
      for(var j in rawRoutes[i]){
        var route = rawRoutes[i][j];
        if(method == 'all'){
          ['get', 'post', 'put', 'patch', 'delete'].forEach(function(verb){
            api.routes.routes[verb].push({ path: route.path, action: route.action });
          });
        } else {
          api.routes.routes[method].push({ path: route.path, action: route.action });
        }
        var words = route.path.split('/');
        words.forEach(function(word){
          if(word[0] === ':'){
            var cleanedWord = word.replace(':', '');
            api.params.postVariables.push(cleanedWord);
          }
        });
        counter++;
      }
    }
    api.params.postVariables = api.utils.arrayUniqueify(api.params.postVariables)
    api.log(counter + ' routes loaded from ' + api.routes.routesFile, 'debug', api.routes.routes);
  };

  api.watchFileAndAct(api.routes.routesFile, function(){
    api.routes.loadRoutes();
  });

  api.routes.loadRoutes();
  next();
}

/////////////////////////////////////////////////////////////////////
// exports
exports.routes = routes;
<commit_msg>do not crash on malformed URL<commit_after>var fs = require('fs');

var routes = function(api, next){

  api.routes = {};
  api.routes.routes = {};
  api.routes.routesFile = api.project_root + '/routes.js';

  ////////////////////////////////////////////////////////////////////////////
  // route processing for web clients
  api.routes.processRoute = function(connection){
    if(connection.params['action'] == null || typeof api.actions.actions[connection.params['action']] === 'undefined'){
      var method = connection.rawConnection.method.toLowerCase();
      for(var i in api.routes.routes[method]){
        var route = api.routes.routes[method][i];
        var match = api.routes.matchURL(connection.rawConnection.parsedURL.pathname, route.path);
        if(match.match === true){
          for(var param in match.params){
            try{
              var decodedName = decodeURIComponent(param.replace(/\+/g, ' '));
              var decodedValue = decodeURIComponent(match.params[param].replace(/\+/g, ' '));
              connection.params[decodedName] = decodedValue;
            }catch(e){
              // malformed URL
            }
          }
          connection.params['action'] = route.action;
          break;
        }
      }
    }
  }

  api.routes.matchURL = function(url, match){
    var response = {match: false, params: {} }
    var urlParts = url.split('/');
    var matchParts = match.split('/');
    var regexp = '';
    var variable = '';
    if(urlParts[0] == ''){ urlParts.splice(0, 1) }
    if(matchParts[0] == ''){ matchParts.splice(0, 1) }
    if(urlParts[(urlParts.length - 1)] == ''){ urlParts.pop() }
    if(matchParts[(matchParts.length - 1)] == ''){ matchParts.pop() }
    if(urlParts[0] == api.config.servers.web.urlPathForActions){ urlParts.splice(0, 1) }
    for(var i in matchParts){
      var part = matchParts[i];
      if(!urlParts[i]){
        return response;
      } else if(part[0] === ':' && part.indexOf('(') < 0){
        variable = part.replace(':', '');
        response.params[variable] = urlParts[i];
      } else if(part[0] === ':' && part.indexOf('(') >= 0){
        variable = part.replace(':', '').split('(')[0];
        regexp = part.split('(')[1];
        var matches = urlParts[i].match(new RegExp(regexp.substring(0, regexp.length - 1), 'g'));
        if(matches != null){
          response.params[variable] = urlParts[i];
        } else {
          return response;
        }
      } else {
        if(urlParts[i] == null || urlParts[i].toLowerCase() != matchParts[i].toLowerCase()){
          return response;
        }
      }
    }
    response.match = true;
    return response;
  }

  // load in the routes file
  api.routes.loadRoutes = function(rawRoutes){
    var counter = 0;
    api.routes.routes = { 'get': [], 'post': [], 'put': [], 'patch' : [], 'delete': [] };

    if(rawRoutes == null){
      if(fs.existsSync(api.routes.routesFile)){
        delete require.cache[require.resolve(api.routes.routesFile)];
        rawRoutes = require(api.routes.routesFile).routes;
      } else {
        api.log('no routes file found, skipping', 'debug');
        return;
      }
    }
    for(var i in rawRoutes){
      var method = i.toLowerCase();
      for(var j in rawRoutes[i]){
        var route = rawRoutes[i][j];
        if(method == 'all'){
          ['get', 'post', 'put', 'patch', 'delete'].forEach(function(verb){
            api.routes.routes[verb].push({ path: route.path, action: route.action });
          });
        } else {
          api.routes.routes[method].push({ path: route.path, action: route.action });
        }
        var words = route.path.split('/');
        words.forEach(function(word){
          if(word[0] === ':'){
            var cleanedWord = word.replace(':', '');
            api.params.postVariables.push(cleanedWord);
          }
        });
        counter++;
      }
    }
    api.params.postVariables = api.utils.arrayUniqueify(api.params.postVariables)
    api.log(counter + ' routes loaded from ' + api.routes.routesFile, 'debug', api.routes.routes);
  };

  api.watchFileAndAct(api.routes.routesFile, function(){
    api.routes.loadRoutes();
  });

  api.routes.loadRoutes();
  next();
}

/////////////////////////////////////////////////////////////////////
// exports
exports.routes = routes;
<|endoftext|>


<commit_before><commit_msg>Create jsonp.js<commit_after><|endoftext|>


<commit_before>(function() {
    'use strict';
    var express = require('express');

    var routes = function(Book) {
        var bookRouter = express.Router();

        bookRouter.route('/')
            .post(function(req, res) {
                var book = new Book(req.body);
                book.save();
                res.status(201).send(book);
            })
            .get(function(req, res) {
                var query = {};
                // support of queries such as: http://localhost:8080/api/books?genre=Historical%20Fiction
                if (req.query.genre) {
                    query.genre = req.query.genre;
                }
                if (req.query.author) {
                    query.author = req.query.author;
                }

                Book.find(query, function(err, books) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(books);
                    }
                });
            });

        bookRouter.use('/:bookId', function(req, res, next) {
            Book.findById(req.params.bookId, function(err, book) {
                if (err) {
                    res.status(500).send(err);
                } else if (book) {
                    req.book = book;
                    next();
                } else {
                    res.status(404).send('no book found!');
                }
            });
        });

        bookRouter.route('/:bookId')
            //support a query by ID, for example: http://localhost:8080/api/books/58a747202ba0ef40e1188b2b
            .get(function(req, res) {
                res.json(req.book);
            })
            .put(function(req, res) {
                req.book.title = req.body.title;
                req.book.author = req.body.author;
                req.book.genre = req.body.genre;
                req.book.read = req.body.read;
                req.book.save(function(err) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(req.book);
                    }
                });
            })
            .patch(function(req, res) {
                if (req.body._id) {
                    delete req.body._id;
                }
                for (var key in req.body) {
                    if (req.body.hasOwnProperty(key)) {
                        req.book[key] = req.body[key];
                    }
                }
                req.book.save(function(err) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(req.book);
                    }
                });
            });

        return bookRouter;
    };

    module.exports = routes;

}());<commit_msg>Implemented DELETE method.<commit_after>(function() {
    'use strict';
    var express = require('express');

    var routes = function(Book) {
        var bookRouter = express.Router();

        bookRouter.route('/')
            .post(function(req, res) {
                // Create a new book
                var book = new Book(req.body);
                book.save();
                res.status(201).send(book);
            })
            .get(function(req, res) {
                // Get all books
                var query = {};
                // support of queries such as: http://localhost:8080/api/books?genre=Historical%20Fiction
                if (req.query.genre) {
                    query.genre = req.query.genre;
                }
                if (req.query.author) {
                    query.author = req.query.author;
                }

                Book.find(query, function(err, books) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(books);
                    }
                });
            });

        bookRouter.use('/:bookId', function(req, res, next) {
            Book.findById(req.params.bookId, function(err, book) {
                if (err) {
                    res.status(500).send(err);
                } else if (book) {
                    req.book = book;
                    next();
                } else {
                    res.status(404).send('no book found!');
                }
            });
        });

        bookRouter.route('/:bookId')
            //support a query by ID, for example: http://localhost:8080/api/books/58a747202ba0ef40e1188b2b
            .get(function(req, res) {
                // Get an specific book by _id
                res.json(req.book);
            })
            .put(function(req, res) {
                // Change/update an specific book by _id (replaces all attributes)
                req.book.title = req.body.title;
                req.book.author = req.body.author;
                req.book.genre = req.body.genre;
                req.book.read = req.body.read;
                req.book.save(function(err) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(req.book);
                    }
                });
            })
            .patch(function(req, res) {
                // Change/update an attribute from an specific book by _id
                if (req.body._id) {
                    delete req.body._id;
                }
                for (var key in req.body) {
                    if (req.body.hasOwnProperty(key)) {
                        req.book[key] = req.body[key];
                    }
                }
                req.book.save(function(err) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.json(req.book);
                    }
                });
            })
            .delete(function(req, res) {
                req.book.remove(function(err) {
                    if (err) {
                        res.status(500).send(err);
                    } else {
                        res.status(204).send('Removed');
                    }
                });
            });

        return bookRouter;
    };

    module.exports = routes;

}());<|endoftext|>


<commit_before>// --- npm init -> package.json
// [npm install --save-dev gulp] -> 核心
// [npm install --save-dev gulp del] -> 删除
// [npm install --save-dev gulp-if] -> 条件
// [npm install --save-dev gulp-filter] -> 过滤器
// [npm install --save-dev run-sequence] -> 顺序执行
// [npm install --save-dev jshint] -> JS校验核心
// [npm install --save-dev gulp-jshint] -> JS校验
// [npm install --save-dev gulp-file-include] -> HTML合并
// [npm install --save-dev gulp-replace] -> 替换文本
// [npm install --save-dev gulp-htmlmin] -> 压缩HTML
// [npm install --save-dev gulp-concat] -> 合并JS
// [npm install --save-dev gulp-uglify] -> 压缩JS
// [npm install --save-dev gulp-concat-css] -> 合并CSS
// [npm install --save-dev gulp-clean-css] -> 压缩CSS
// [npm install --save-dev gulp-rev] -> 给静态资源加MD5
// [npm install --save-dev gulp-rev-collector] -> 替换静态资源引用
// [npm install --save-dev gulp-connect] -> 静态资源服务器

var gulp = require('gulp');
var runSequence = require('run-sequence');
var connect = require('gulp-connect');
var del = require('del');
var gulpif = require('gulp-if');
var filter = require('gulp-filter');
var jshint = require('gulp-jshint');
var fileinclude = require('gulp-file-include');
var replace = require('gulp-replace');
var htmlmin = require('gulp-htmlmin');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var concatCss = require('gulp-concat-css');
var cleanCSS = require('gulp-clean-css');
var rev = require('gulp-rev');
var revCollector = require('gulp-rev-collector');

var _environment = {
    dev: false,
    staticBase: 'http://static.tc.com',
    openApiBase: 'http://openapi.tc.com',
    localhostHost: 'localhost',
    localhostPort: 10010
};

// 默认开发模式下编译
gulp.task('default', function (cb) {
    _environment.dev = true;
    task(cb);
});

// 本地模式下编译
gulp.task('local', function (cb) {
    _environment.dev = true;
    _environment.staticBase = 'http://' + _environment.localhostHost + ':' + _environment.localhostPort;
    task(cb);
});

// 生产模式下编译
gulp.task('pdu', function (cb) {
    task(cb);
});

var task = function (cb) {
    runSequence(
        'clean',
        'javascript',
        'stylesheet',
        'mg-script-libs',
        'mg-style-libs',
        'libs-static',
        'libs-other',
        'images',
        'html',
        'special',
        cb);
};

// 清理工作空间
gulp.task('clean', function (cb) {
    return del([
        'dist/**',
        'manifests/**'
    ], cb);
});

// 编译javascript
gulp.task('javascript', function () {
    return gulp
        .src('src/resources/scripts/**/*.js')
        // 静态检查
        .pipe(jshint())
        .pipe(jshint.reporter('default'))
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, uglify()))
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/scripts'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/scripts'));
});

// 编译stylesheet
gulp.task('stylesheet', function () {
    return gulp
        .src('src/resources/styles/**/*.css')
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, cleanCSS()))
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/styles'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/styles'));
});

// 合并javascript-lib
gulp.task('mg-script-libs', function (cb) {
    del('src/resources/libs/_post-lib.min.js', cb);
    return gulp
        .src([
            'src/resources/libs/jquery-3.1.0.min.js',
            'src/resources/libs/vue.min.js',
            'src/resources/libs/axios.min.js',
            'src/resources/libs/underscore.min.js',
            'src/resources/libs/assets/js/amazeui.min.js',
            'src/resources/libs/layer/layer.min.js'
        ])
        .pipe(concat('_post-lib.min.js'))
        .pipe(gulp.dest('src/resources/libs'));
});

// 合并stylesheet-lib
gulp.task('mg-style-libs', function (cb) {
    del('src/resources/libs/_pre-lib.min.css', cb);
    return gulp
        .src([
            'src/resources/libs/normalize.min.css',
            'src/resources/libs/assets/css/amazeui.flat.min.css'
        ])
        .pipe(concatCss("_pre-lib.min.css"))
        .pipe(gulp.dest('src/resources/libs'));
});

// 编译libs-static
gulp.task('libs-static', function () {
    // 所有libs均已压缩
    return gulp
        .src([
            // libs不打包为min文件不打包
            'src/resources/libs/**/*.min.js',
            'src/resources/libs/**/*.min.css'
        ])
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/libs'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/libs'));
});

// 编译libs-other
gulp.task('libs-other', function () {
    return gulp
        .src([
            'src/resources/libs/**/*.*',
            '!src/resources/libs/**/*.js',
            '!src/resources/libs/**/*.css'])
        .pipe(gulp.dest('dist/resources/libs'));
});

// 编译images
gulp.task('images', function () {
    return gulp
        .src('src/resources/images/**/*.*')
        .pipe(gulp.dest('dist/resources/images'));
});

// 编译html
gulp.task('html', function () {
    return gulp
        .src(['manifest/**/*.json', 'src/html/**/*.html'])
        // 包含文件
        .pipe(fileinclude({
            prefix: '@@',
            basepath: '@root'
        }))
        // 替换静态文件引用
        .pipe(revCollector({
            replaceReved: true
        }))
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 替换后端接口openApiBase
        .pipe(replace(/\$\{openApiBase}/g, _environment.openApiBase))
        // 替换静态资源staticBase
        .pipe(replace(/\$\{staticBase}/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, htmlmin({
            //清除HTML注释
            removeComments: true,
            //压缩HTML
            collapseWhitespace: true,
            //压缩页面JS
            minifyJS: true,
            //压缩页面CSS
            minifyCSS: true
        })))
        .pipe(gulp.dest('dist/html'));
});

// 编译特殊文件
gulp.task('special', function () {
    return gulp
    // 因为静态资源合并 相对路径发生改变 手动加载此文件
        .src('src/resources/libs/layer/skin/default/layer.css')
        .pipe(gulp.dest('dist/resources/libs/skin/default'));
});

// ----- 服务器开始
gulp.task('start', ['local'], function () {
    connect.server({
        name: 'tc-static',
        root: ['dist/html', 'dist/resources'],
        port: _environment.localhostPort,
        livereload: true
    });
});
<commit_msg>project optimize (113)<commit_after>// --- npm init -> package.json
// [npm install --save-dev gulp] -> 核心
// [npm install --save-dev gulp del] -> 删除
// [npm install --save-dev gulp-if] -> 条件
// [npm install --save-dev gulp-filter] -> 过滤器
// [npm install --save-dev run-sequence] -> 顺序执行
// [npm install --save-dev jshint] -> JS校验核心
// [npm install --save-dev gulp-jshint] -> JS校验
// [npm install --save-dev gulp-file-include] -> HTML合并
// [npm install --save-dev gulp-replace] -> 替换文本
// [npm install --save-dev gulp-htmlmin] -> 压缩HTML
// [npm install --save-dev gulp-concat] -> 合并JS
// [npm install --save-dev gulp-uglify] -> 压缩JS
// [npm install --save-dev gulp-concat-css] -> 合并CSS
// [npm install --save-dev gulp-clean-css] -> 压缩CSS
// [npm install --save-dev gulp-rev] -> 给静态资源加MD5
// [npm install --save-dev gulp-rev-collector] -> 替换静态资源引用
// [npm install --save-dev gulp-connect] -> 静态资源服务器

var gulp = require('gulp');
var runSequence = require('run-sequence');
var connect = require('gulp-connect');
var del = require('del');
var gulpif = require('gulp-if');
var filter = require('gulp-filter');
var jshint = require('gulp-jshint');
var fileInclude = require('gulp-file-include');
var replace = require('gulp-replace');
var htmlmin = require('gulp-htmlmin');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var concatCss = require('gulp-concat-css');
var cleanCSS = require('gulp-clean-css');
var rev = require('gulp-rev');
var revCollector = require('gulp-rev-collector');

var _environment = {
    dev: false,
    staticBase: 'http://static.tc.com',
    openApiBase: 'http://openapi.tc.com',
    localhostHost: 'localhost',
    localhostPort: 10010
};

// 默认开发模式下编译
gulp.task('default', function (cb) {
    _environment.dev = true;
    task(cb);
});

// 本地模式下编译
gulp.task('local', function (cb) {
    _environment.dev = true;
    _environment.staticBase = 'http://' + _environment.localhostHost + ':' + _environment.localhostPort;
    task(cb);
});

// 生产模式下编译
gulp.task('pdu', function (cb) {
    task(cb);
});

var task = function (cb) {
    runSequence(
        'clean',
        'javascript',
        'stylesheet',
        'mg-script-libs',
        'mg-style-libs',
        'libs-static',
        'libs-other',
        'images',
        'html',
        'special',
        cb);
};

// 清理工作空间
gulp.task('clean', function (cb) {
    return del([
        'dist/**',
        'manifests/**'
    ], cb);
});

// 编译javascript
gulp.task('javascript', function () {
    return gulp
        .src('src/resources/scripts/**/*.js')
        // 静态检查
        .pipe(jshint())
        .pipe(jshint.reporter('default'))
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, uglify()))
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/scripts'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/scripts'));
});

// 编译stylesheet
gulp.task('stylesheet', function () {
    return gulp
        .src('src/resources/styles/**/*.css')
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, cleanCSS()))
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/styles'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/styles'));
});

// 合并javascript-lib
gulp.task('mg-script-libs', function (cb) {
    del('src/resources/libs/_post-lib.min.js', cb);
    return gulp
        .src([
            'src/resources/libs/jquery-3.1.0.min.js',
            'src/resources/libs/vue.min.js',
            'src/resources/libs/axios.min.js',
            'src/resources/libs/underscore.min.js',
            'src/resources/libs/assets/js/amazeui.min.js',
            'src/resources/libs/layer/layer.min.js'
        ])
        .pipe(concat('_post-lib.min.js'))
        .pipe(gulp.dest('src/resources/libs'));
});

// 合并stylesheet-lib
gulp.task('mg-style-libs', function (cb) {
    del('src/resources/libs/_pre-lib.min.css', cb);
    return gulp
        .src([
            'src/resources/libs/normalize.min.css',
            'src/resources/libs/assets/css/amazeui.flat.min.css'
        ])
        .pipe(concatCss("_pre-lib.min.css"))
        .pipe(gulp.dest('src/resources/libs'));
});

// 编译libs-static
gulp.task('libs-static', function () {
    // 所有libs均已压缩
    return gulp
        .src([
            // libs不打包为min文件不打包
            'src/resources/libs/**/*.min.js',
            'src/resources/libs/**/*.min.css'
        ])
        // 加版本号
        .pipe(rev())
        // 输出
        .pipe(gulp.dest('dist/resources/libs'))
        // 生成 manifest
        .pipe(rev.manifest())
        .pipe(gulp.dest('manifest/libs'));
});

// 编译libs-other
gulp.task('libs-other', function () {
    return gulp
        .src([
            'src/resources/libs/**/*.*',
            '!src/resources/libs/**/*.js',
            '!src/resources/libs/**/*.css'])
        .pipe(gulp.dest('dist/resources/libs'));
});

// 编译images
gulp.task('images', function () {
    return gulp
        .src('src/resources/images/**/*.*')
        .pipe(gulp.dest('dist/resources/images'));
});

// 编译html
gulp.task('html', function () {
    return gulp
        .src(['manifest/**/*.json', 'src/html/**/*.html'])
        // 包含文件
        .pipe(fileInclude({
            prefix: '@@',
            basepath: '@root'
        }))
        // 替换静态文件引用
        .pipe(revCollector({
            replaceReved: true
        }))
        // 替换静态资源前缀路径
        .pipe(replace(/\/tc-static\/src\/resources/g, _environment.staticBase))
        // 替换后端接口openApiBase
        .pipe(replace(/\$\{openApiBase}/g, _environment.openApiBase))
        // 替换静态资源staticBase
        .pipe(replace(/\$\{staticBase}/g, _environment.staticBase))
        // 压缩(开发不压缩)
        .pipe(gulpif(!_environment.dev, htmlmin({
            //清除HTML注释
            removeComments: true,
            //压缩HTML
            collapseWhitespace: true,
            //压缩页面JS
            minifyJS: true,
            //压缩页面CSS
            minifyCSS: true
        })))
        .pipe(gulp.dest('dist/html'));
});

// 编译特殊文件
gulp.task('special', function () {
    return gulp
    // 因为静态资源合并 相对路径发生改变 手动加载此文件
        .src('src/resources/libs/layer/skin/default/layer.css')
        .pipe(gulp.dest('dist/resources/libs/skin/default'));
});

// ----- 服务器
gulp.task('start', ['local'], function () {
    connect.server({
        name: 'tc-static',
        root: ['dist/html', 'dist/resources'],
        port: _environment.localhostPort,
        livereload: true
    });
});
<|endoftext|>