aboutsummaryrefslogtreecommitdiff
path: root/node_modules/jake/lib/task/task.js
diff options
context:
space:
mode:
authorMinteck <minteck@phoenixnet.tech>2021-10-17 20:08:04 +0200
committerMinteck <minteck@phoenixnet.tech>2021-10-17 20:08:04 +0200
commit3a94acf27ea1eff8bd1125450f07c0c366332e80 (patch)
tree5bd619df0b7e04afc174df98736a527477522acb /node_modules/jake/lib/task/task.js
downloadwolfeye-js-3a94acf27ea1eff8bd1125450f07c0c366332e80.tar.gz
wolfeye-js-3a94acf27ea1eff8bd1125450f07c0c366332e80.tar.bz2
wolfeye-js-3a94acf27ea1eff8bd1125450f07c0c366332e80.zip
Initial commit
Diffstat (limited to 'node_modules/jake/lib/task/task.js')
-rw-r--r--node_modules/jake/lib/task/task.js439
1 files changed, 439 insertions, 0 deletions
diff --git a/node_modules/jake/lib/task/task.js b/node_modules/jake/lib/task/task.js
new file mode 100644
index 0000000..9e8886f
--- /dev/null
+++ b/node_modules/jake/lib/task/task.js
@@ -0,0 +1,439 @@
+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();
+ }
+
+ _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(':');
+ }
+
+ 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');
+