MACHIN
This commit is contained in:
		
							
								
								
									
										449
									
								
								32/node_modules/jake/lib/task/task.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										449
									
								
								32/node_modules/jake/lib/task/task.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,449 @@
 | 
			
		||||
let EventEmitter = require('events').EventEmitter;
 | 
			
		||||
let async = require('async');
 | 
			
		||||
let chalk = require('chalk');
 | 
			
		||||
// 'rule' module is required at the bottom because circular deps
 | 
			
		||||
 | 
			
		||||
// Used for task value, so better not to use
 | 
			
		||||
// null, since value should be unset/uninitialized
 | 
			
		||||
let UNDEFINED_VALUE;
 | 
			
		||||
 | 
			
		||||
const ROOT_TASK_NAME = '__rootTask__';
 | 
			
		||||
const POLLING_INTERVAL = 100;
 | 
			
		||||
 | 
			
		||||
// Parse any positional args attached to the task-name
 | 
			
		||||
function parsePrereqName(name) {
 | 
			
		||||
  let taskArr = name.split('[');
 | 
			
		||||
  let taskName = taskArr[0];
 | 
			
		||||
  let taskArgs = [];
 | 
			
		||||
  if (taskArr[1]) {
 | 
			
		||||
    taskArgs = taskArr[1].replace(/\]$/, '');
 | 
			
		||||
    taskArgs = taskArgs.split(',');
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    name: taskName,
 | 
			
		||||
    args: taskArgs
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  @name jake.Task
 | 
			
		||||
  @class
 | 
			
		||||
  @extends EventEmitter
 | 
			
		||||
  @description A Jake Task
 | 
			
		||||
 | 
			
		||||
  @param {String} name The name of the Task
 | 
			
		||||
  @param {Array} [prereqs] Prerequisites to be run before this task
 | 
			
		||||
  @param {Function} [action] The action to perform for this task
 | 
			
		||||
  @param {Object} [opts]
 | 
			
		||||
    @param {Array} [opts.asyc=false] Perform this task asynchronously.
 | 
			
		||||
    If you flag a task with this option, you must call the global
 | 
			
		||||
    `complete` method inside the task's action, for execution to proceed
 | 
			
		||||
    to the next task.
 | 
			
		||||
 */
 | 
			
		||||
class Task extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
  constructor(name, prereqs, action, options) {
 | 
			
		||||
    // EventEmitter ctor takes no args
 | 
			
		||||
    super();
 | 
			
		||||
 | 
			
		||||
    if (name.indexOf(':') > -1) {
 | 
			
		||||
      throw new Error('Task name cannot include a colon. It is used internally as namespace delimiter.');
 | 
			
		||||
    }
 | 
			
		||||
    let opts = options || {};
 | 
			
		||||
 | 
			
		||||
    this._currentPrereqIndex = 0;
 | 
			
		||||
    this._internal = false;
 | 
			
		||||
    this._skipped = false;
 | 
			
		||||
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.prereqs = prereqs;
 | 
			
		||||
    this.action = action;
 | 
			
		||||
    this.async = false;
 | 
			
		||||
    this.taskStatus = Task.runStatuses.UNSTARTED;
 | 
			
		||||
    this.description = null;
 | 
			
		||||
    this.args = [];
 | 
			
		||||
    this.value = UNDEFINED_VALUE;
 | 
			
		||||
    this.concurrency = 1;
 | 
			
		||||
    this.startTime = null;
 | 
			
		||||
    this.endTime = null;
 | 
			
		||||
    this.directory = null;
 | 
			
		||||
    this.namespace = null;
 | 
			
		||||
 | 
			
		||||
    // Support legacy async-flag -- if not explicitly passed or falsy, will
 | 
			
		||||
    // be set to empty-object
 | 
			
		||||
    if (typeof opts == 'boolean' && opts === true) {
 | 
			
		||||
      this.async = true;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      if (opts.async) {
 | 
			
		||||
        this.async = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (opts.concurrency) {
 | 
			
		||||
        this.concurrency = opts.concurrency;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //Do a test on self dependencies for this task
 | 
			
		||||
    if(Array.isArray(this.prereqs) && this.prereqs.indexOf(this.name) !== -1) {
 | 
			
		||||
      throw new Error("Cannot use prereq " + this.name + " as a dependency of itself");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get fullName() {
 | 
			
		||||
    return this._getFullName();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get params() {
 | 
			
		||||
    return this._getParams();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _initInvocationChain() {
 | 
			
		||||
    // Legacy global invocation chain
 | 
			
		||||
    jake._invocationChain.push(this);
 | 
			
		||||
 | 
			
		||||
    // New root chain
 | 
			
		||||
    if (!this._invocationChain) {
 | 
			
		||||
      this._invocationChainRoot = true;
 | 
			
		||||
      this._invocationChain = [];
 | 
			
		||||
      if (jake.currentRunningTask) {
 | 
			
		||||
        jake.currentRunningTask._waitForChains = jake.currentRunningTask._waitForChains || [];
 | 
			
		||||
        jake.currentRunningTask._waitForChains.push(this._invocationChain);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    @name jake.Task#invoke
 | 
			
		||||
    @function
 | 
			
		||||
    @description Runs prerequisites, then this task. If the task has already
 | 
			
		||||
    been run, will not run the task again.
 | 
			
		||||
   */
 | 
			
		||||
  invoke() {
 | 
			
		||||
    this._initInvocationChain();
 | 
			
		||||
 | 
			
		||||
    this.args = Array.prototype.slice.call(arguments);
 | 
			
		||||
    this.reenabled = false;
 | 
			
		||||
    this.runPrereqs();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    @name jake.Task#execute
 | 
			
		||||
    @function
 | 
			
		||||
    @description Run only this task, without prereqs. If the task has already
 | 
			
		||||
    been run, *will* run the task again.
 | 
			
		||||
   */
 | 
			
		||||
  execute() {
 | 
			
		||||
    this._initInvocationChain();
 | 
			
		||||
 | 
			
		||||
    this.args = Array.prototype.slice.call(arguments);
 | 
			
		||||
    this.reenable();
 | 
			
		||||
    this.reenabled = true;
 | 
			
		||||
    this.run();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  runPrereqs() {
 | 
			
		||||
    if (this.prereqs && this.prereqs.length) {
 | 
			
		||||
 | 
			
		||||
      if (this.concurrency > 1) {
 | 
			
		||||
        async.eachLimit(this.prereqs, this.concurrency,
 | 
			
		||||
 | 
			
		||||
          (name, cb) => {
 | 
			
		||||
            let parsed = parsePrereqName(name);
 | 
			
		||||
 | 
			
		||||
            let prereq = this.namespace.resolveTask(parsed.name) ||
 | 
			
		||||
          jake.attemptRule(name, this.namespace, 0) ||
 | 
			
		||||
          jake.createPlaceholderFileTask(name, this.namespace);
 | 
			
		||||
 | 
			
		||||
            if (!prereq) {
 | 
			
		||||
              throw new Error('Unknown task "' + name + '"');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Test for circular invocation
 | 
			
		||||
            if(prereq === this) {
 | 
			
		||||
              setImmediate(function () {
 | 
			
		||||
                cb(new Error("Cannot use prereq " + prereq.name + " as a dependency of itself"));
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (prereq.taskStatus == Task.runStatuses.DONE) {
 | 
			
		||||
            //prereq already done, return
 | 
			
		||||
              setImmediate(cb);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
            //wait for complete before calling cb
 | 
			
		||||
              prereq.once('_done', () => {
 | 
			
		||||
                prereq.removeAllListeners('_done');
 | 
			
		||||
                setImmediate(cb);
 | 
			
		||||
              });
 | 
			
		||||
              // Start the prereq if we are the first to encounter it
 | 
			
		||||
              if (prereq.taskStatus === Task.runStatuses.UNSTARTED) {
 | 
			
		||||
                prereq.taskStatus = Task.runStatuses.STARTED;
 | 
			
		||||
                prereq.invoke.apply(prereq, parsed.args);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
 | 
			
		||||
          (err) => {
 | 
			
		||||
          //async callback is called after all prereqs have run.
 | 
			
		||||
            if (err) {
 | 
			
		||||
              throw err;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              setImmediate(this.run.bind(this));
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        setImmediate(this.nextPrereq.bind(this));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      setImmediate(this.run.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  nextPrereq() {
 | 
			
		||||
    let self = this;
 | 
			
		||||
    let index = this._currentPrereqIndex;
 | 
			
		||||
    let name = this.prereqs[index];
 | 
			
		||||
    let prereq;
 | 
			
		||||
    let parsed;
 | 
			
		||||
 | 
			
		||||
    if (name) {
 | 
			
		||||
 | 
			
		||||
      parsed = parsePrereqName(name);
 | 
			
		||||
 | 
			
		||||
      prereq = this.namespace.resolveTask(parsed.name) ||
 | 
			
		||||
          jake.attemptRule(name, this.namespace, 0) ||
 | 
			
		||||
          jake.createPlaceholderFileTask(name, this.namespace);
 | 
			
		||||
 | 
			
		||||
      if (!prereq) {
 | 
			
		||||
        throw new Error('Unknown task "' + name + '"');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Do when done
 | 
			
		||||
      if (prereq.taskStatus == Task.runStatuses.DONE) {
 | 
			
		||||
        self.handlePrereqDone(prereq);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        prereq.once('_done', () => {
 | 
			
		||||
          this.handlePrereqDone(prereq);
 | 
			
		||||
          prereq.removeAllListeners('_done');
 | 
			
		||||
        });
 | 
			
		||||
        if (prereq.taskStatus == Task.runStatuses.UNSTARTED) {
 | 
			
		||||
          prereq.taskStatus = Task.runStatuses.STARTED;
 | 
			
		||||
          prereq._invocationChain = this._invocationChain;
 | 
			
		||||
          prereq.invoke.apply(prereq, parsed.args);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    @name jake.Task#reenable
 | 
			
		||||
    @function
 | 
			
		||||
    @description Reenables a task so that it can be run again.
 | 
			
		||||
   */
 | 
			
		||||
  reenable(deep) {
 | 
			
		||||
    let prereqs;
 | 
			
		||||
    let prereq;
 | 
			
		||||
    this._skipped = false;
 | 
			
		||||
    this.taskStatus = Task.runStatuses.UNSTARTED;
 | 
			
		||||
    this.value = UNDEFINED_VALUE;
 | 
			
		||||
    if (deep && this.prereqs) {
 | 
			
		||||
      prereqs = this.prereqs;
 | 
			
		||||
      for (let i = 0, ii = prereqs.length; i < ii; i++) {
 | 
			
		||||
        prereq = jake.Task[prereqs[i]];
 | 
			
		||||
        if (prereq) {
 | 
			
		||||
          prereq.reenable(deep);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handlePrereqDone(prereq) {
 | 
			
		||||
    this._currentPrereqIndex++;
 | 
			
		||||
    if (this._currentPrereqIndex < this.prereqs.length) {
 | 
			
		||||
      setImmediate(this.nextPrereq.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      setImmediate(this.run.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isNeeded() {
 | 
			
		||||
    let needed = true;
 | 
			
		||||
    if (this.taskStatus == Task.runStatuses.DONE) {
 | 
			
		||||
      needed = false;
 | 
			
		||||
    }
 | 
			
		||||
    return needed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  run() {
 | 
			
		||||
    let val, previous;
 | 
			
		||||
    let hasAction = typeof this.action == 'function';
 | 
			
		||||
 | 
			
		||||
    if (!this.isNeeded()) {
 | 
			
		||||
      this.emit('skip');
 | 
			
		||||
      this.emit('_done');
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      if (this._invocationChain.length) {
 | 
			
		||||
        previous = this._invocationChain[this._invocationChain.length - 1];
 | 
			
		||||
        // If this task is repeating and its previous is equal to this, don't check its status because it was set to UNSTARTED by the reenable() method
 | 
			
		||||
        if (!(this.reenabled && previous == this)) {
 | 
			
		||||
          if (previous.taskStatus != Task.runStatuses.DONE) {
 | 
			
		||||
            let now = (new Date()).getTime();
 | 
			
		||||
            if (now - this.startTime > jake._taskTimeout) {
 | 
			
		||||
              return jake.fail(`Timed out waiting for task: ${previous.name} with status of ${previous.taskStatus}`);
 | 
			
		||||
            }
 | 
			
		||||
            setTimeout(this.run.bind(this), POLLING_INTERVAL);
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (!(this.reenabled && previous == this)) {
 | 
			
		||||
        this._invocationChain.push(this);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!(this._internal || jake.program.opts.quiet)) {
 | 
			
		||||
        console.log("Starting '" + chalk.green(this.fullName) + "'...");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.startTime = (new Date()).getTime();
 | 
			
		||||
      this.emit('start');
 | 
			
		||||
 | 
			
		||||
      jake.currentRunningTask = this;
 | 
			
		||||
 | 
			
		||||
      if (hasAction) {
 | 
			
		||||
        try {
 | 
			
		||||
          if (this.directory) {
 | 
			
		||||
            process.chdir(this.directory);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          val = this.action.apply(this, this.args);
 | 
			
		||||
 | 
			
		||||
          if (typeof val == 'object' && typeof val.then == 'function') {
 | 
			
		||||
            this.async = true;
 | 
			
		||||
 | 
			
		||||
            val.then(
 | 
			
		||||
              (result) => {
 | 
			
		||||
                setImmediate(() => {
 | 
			
		||||
                  this.complete(result);
 | 
			
		||||
                });
 | 
			
		||||
              },
 | 
			
		||||
              (err) => {
 | 
			
		||||
                setImmediate(() => {
 | 
			
		||||
                  this.errorOut(err);
 | 
			
		||||
                });
 | 
			
		||||
              });
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        catch (err) {
 | 
			
		||||
          this.errorOut(err);
 | 
			
		||||
          return; // Bail out, not complete
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!(hasAction && this.async)) {
 | 
			
		||||
        setImmediate(() => {
 | 
			
		||||
          this.complete(val);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  errorOut(err) {
 | 
			
		||||
    this.taskStatus = Task.runStatuses.ERROR;
 | 
			
		||||
    this._invocationChain.chainStatus = Task.runStatuses.ERROR;
 | 
			
		||||
    this.emit('error', err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  complete(val) {
 | 
			
		||||
 | 
			
		||||
    if (Array.isArray(this._waitForChains)) {
 | 
			
		||||
      let stillWaiting = this._waitForChains.some((chain) => {
 | 
			
		||||
        return !(chain.chainStatus == Task.runStatuses.DONE ||
 | 
			
		||||
              chain.chainStatus == Task.runStatuses.ERROR);
 | 
			
		||||
      });
 | 
			
		||||
      if (stillWaiting) {
 | 
			
		||||
        let now = (new Date()).getTime();
 | 
			
		||||
        let elapsed = now - this.startTime;
 | 
			
		||||
        if (elapsed > jake._taskTimeout) {
 | 
			
		||||
          return jake.fail(`Timed out waiting for task: ${this.name} with status of ${this.taskStatus}. Elapsed: ${elapsed}`);
 | 
			
		||||
        }
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          this.complete(val);
 | 
			
		||||
        }, POLLING_INTERVAL);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    jake._invocationChain.splice(jake._invocationChain.indexOf(this), 1);
 | 
			
		||||
 | 
			
		||||
    if (this._invocationChainRoot) {
 | 
			
		||||
      this._invocationChain.chainStatus = Task.runStatuses.DONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._currentPrereqIndex = 0;
 | 
			
		||||
 | 
			
		||||
    // If 'complete' getting called because task has been
 | 
			
		||||
    // run already, value will not be passed -- leave in place
 | 
			
		||||
    if (!this._skipped) {
 | 
			
		||||
      this.taskStatus = Task.runStatuses.DONE;
 | 
			
		||||
      this.value = val;
 | 
			
		||||
 | 
			
		||||
      this.emit('complete', this.value);
 | 
			
		||||
      this.emit('_done');
 | 
			
		||||
 | 
			
		||||
      this.endTime = (new Date()).getTime();
 | 
			
		||||
      let taskTime = this.endTime - this.startTime;
 | 
			
		||||
 | 
			
		||||
      if (!(this._internal || jake.program.opts.quiet)) {
 | 
			
		||||
        console.log("Finished '" + chalk.green(this.fullName) + "' after " + chalk.magenta(taskTime + ' ms'));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _getFullName() {
 | 
			
		||||
    let ns = this.namespace;
 | 
			
		||||
    let path = (ns && ns.path) || '';
 | 
			
		||||
    path = (path && path.split(':')) || [];
 | 
			
		||||
    if (this.namespace !== jake.defaultNamespace) {
 | 
			
		||||
      path.push(this.namespace.name);
 | 
			
		||||
    }
 | 
			
		||||
    path.push(this.name);
 | 
			
		||||
    return path.join(':');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _getParams() {
 | 
			
		||||
    if (!this.action) return "";
 | 
			
		||||
    let params = (new RegExp('(?:'+this.action.name+'\\s*|^)\\s*\\((.*?)\\)').exec(this.action.toString().replace(/\n/g, '')) || [''])[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
 | 
			
		||||
    return params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getBaseNamespacePath(fullName) {
 | 
			
		||||
    return fullName.split(':').slice(0, -1).join(':');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getBaseTaskName(fullName) {
 | 
			
		||||
    return fullName.split(':').pop();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Task.runStatuses = {
 | 
			
		||||
  UNSTARTED: 'unstarted',
 | 
			
		||||
  DONE: 'done',
 | 
			
		||||
  STARTED: 'started',
 | 
			
		||||
  ERROR: 'error'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Task.ROOT_TASK_NAME = ROOT_TASK_NAME;
 | 
			
		||||
 | 
			
		||||
exports.Task = Task;
 | 
			
		||||
 | 
			
		||||
// Required here because circular deps
 | 
			
		||||
require('../rule');
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user