Skip to content

Commit

Permalink
Merge d70ecf3 into aebe760
Browse files Browse the repository at this point in the history
  • Loading branch information
mshima committed Feb 4, 2020
2 parents aebe760 + d70ecf3 commit 8dac552
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 65 deletions.
188 changes: 139 additions & 49 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,37 @@ const methodIsValid = function(name) {
return name.charAt(0) !== '_' && name !== 'constructor';
};

/**
* Queue options.
* @typedef {Object} QueueOptions
* @property {string} [queueName] - Name of the queue.
* @property {boolean} [once] - Execute only once by namespace and taskName.
* @property {boolean} [run] - Run the queue if not running yet.
*/

/**
* Task options.
* @typedef {Object} TaskOptions
* @extends QueueOptions
* @property {Function} [reject] - Reject callback.
*/

/**
* Priority object.
* @typedef {Object} Priority
* @extends QueueOptions
* @property {string} priorityName - Name of the priority.
* @property {string} [before] - The queue which this priority should be added before.
*/

/**
* Complete Task object.
* @typedef {Object} Task
* @extends TaskOptions
* @property {Function} method - Function to be queued.
* @property {string} taskName - Name of the task.
*/

/**
* The `Generator` class provides the common API shared by all generators.
* It define options, arguments, file, prompt, log, API, etc.
Expand Down Expand Up @@ -187,24 +218,29 @@ class Generator extends EventEmitter {

// Add original queues.
Generator.queues.forEach(queue => {
this._queues[queue] = queue;
this._queues[queue] = { priorityName: queue, queueName: queue };
});

// Add custom queues
if (Array.isArray(this.options.customPriorities)) {
const customPriorities = this.options.customPriorities;
const customPriorities = this.options.customPriorities.map(customPriority => {
// Keep backward compatibility with name
const newPriority = { priorityName: customPriority.name, ...customPriority };
delete newPriority.name;
return newPriority;
});

// Sort customPriorities, a referenced custom queue must be added before the one that reference it.
customPriorities.sort((a, b) => {
if (a.name === b.name) {
if (a.priorityName === b.priorityName) {
throw new Error(`Duplicate custom queue ${a.name}`);
}

if (a.name === b.before) {
if (a.priorityName === b.before) {
return -1;
}

if (b.name === a.before) {
if (b.priorityName === a.before) {
return 1;
}

Expand All @@ -213,11 +249,13 @@ class Generator extends EventEmitter {

// Add queue to runLoop
customPriorities.forEach(customQueue => {
const queueName = `${this.options.namespace}#${customQueue.name}`;
debug(`Registering custom queue ${queueName}`);
this._queues[customQueue.name] = queueName;
customQueue.queueName =
customQueue.queueName ||
`${this.options.namespace}#${customQueue.priorityName}`;
debug(`Registering custom queue ${customQueue.queueName}`);
this._queues[customQueue.priorityName] = customQueue;

if (this.env.runLoop.queueNames.includes(queueName)) {
if (this.env.runLoop.queueNames.includes(customQueue.queueName)) {
return;
}

Expand Down Expand Up @@ -257,7 +295,10 @@ class Generator extends EventEmitter {
};
}

this.env.runLoop.addSubQueue(queueName, this._queues[customQueue.before]);
let beforeQueue = customQueue.before
? this._queues[customQueue.before].queueName
: undefined;
this.env.runLoop.addSubQueue(customQueue.queueName, beforeQueue);
});
}
}
Expand Down Expand Up @@ -501,9 +542,9 @@ class Generator extends EventEmitter {
* Schedule methods on a run queue.
*
* @param {Function|Object} method: Method to be scheduled or object with function properties.
* @param {String} [methodName]: Name of the method to be scheduled.
* @param {String} [methodName]: Name of the method (task) to be scheduled.
* @param {String} [queueName]: Name of the queue to be scheduled on.
* @param {String} [reject]: Reject callback.
* @param {Function} [reject]: Reject callback.
*/
queueMethod(method, methodName, queueName, reject = () => {}) {
if (typeof queueName === 'function') {
Expand All @@ -513,54 +554,95 @@ class Generator extends EventEmitter {
queueName = queueName || 'default';
}

const self = this;
if (!_.isFunction(method)) {
if (typeof methodName === 'function') {
reject = methodName;
methodName = undefined;
}

queueName = methodName || queueName;
// Run each queue items
_.each(method, (newMethod, newMethodName) => {
if (!_.isFunction(newMethod) || !methodIsValid(newMethodName)) return;

self.queueMethod(newMethod, newMethodName, queueName, reject);
this.queueTaskGroup(method, {
queueName: methodName,
reject
});
return;
}

this.queueTask({
method,
taskName: methodName,
queueName,
reject
});
}

/**
* Schedule methods on a run queue.
*
* @param {Object} taskGroup: Name of the method (task) to be scheduled or taskOptions.
* @param {TaskOptions} [taskOptions]: Name of the method (task) to be scheduled or taskOptions.
*/
queueTaskGroup(taskGroup, taskOptions) {
const self = this;
// Run each queue items
_.each(taskGroup, (newMethod, newMethodName) => {
if (!_.isFunction(newMethod) || !methodIsValid(newMethodName)) return;

self.queueTask({
...taskOptions,
method: newMethod,
taskName: newMethodName
});
});
}

/**
* Schedule tasks on a run queue.
*
* @param {Task} task: Task to be queued.
*/
queueTask(task) {
const reject = task.reject || (() => {});
const queueName = task.queueName || 'default';
const methodName = task.taskName;
const method = task.method;
const once = task.once ? methodName : undefined;

const self = this;
let namespace = '';
if (self.options && self.options.namespace) {
namespace = self.options.namespace;
}

debug(`Queueing ${namespace}#${methodName} in ${queueName}`);
self.env.runLoop.add(queueName, completed => {
debug(`Running ${namespace}#${methodName}`);
self.emit(`method:${methodName}`);

runAsync(function() {
self.async = () => this.async();
self.runningState = { namespace, queueName, methodName };
return method.apply(self, self.args);
})()
.then(function() {
delete self.runningState;
completed();
})
.catch(err => {
debug(`An error occured while running ${namespace}#${methodName}`, err);
delete self.runningState;

// Ensure we emit the error event outside the promise context so it won't be
// swallowed when there's no listeners.
setImmediate(() => {
self.emit('error', err);
reject(err);
debug(`Queueing ${namespace}#${methodName} with options %o`, task);
self.env.runLoop.add(
queueName,
completed => {
debug(`Running ${namespace}#${methodName}`);
self.emit(`method:${methodName}`);

runAsync(function() {
self.async = () => this.async();
self.runningState = { namespace, queueName, methodName };
return method.apply(self, self.args);
})()
.then(function() {
delete self.runningState;
completed();
})
.catch(err => {
debug(`An error occured while running ${namespace}#${methodName}`, err);
delete self.runningState;

// Ensure we emit the error event outside the promise context so it won't be
// swallowed when there's no listeners.
setImmediate(() => {
self.emit('error', err);
reject(err);
});
});
});
});
},
{ once, run: task.run }
);
}

/**
Expand Down Expand Up @@ -606,19 +688,22 @@ class Generator extends EventEmitter {
);
const item = property.value ? property.value : property.get.call(self);

const queueName = self._queues[name];
const priority = self._queues[name];
let taskOptions = { ...priority, reject };

// Name points to a function; run it!
if (typeof item === 'function') {
return self.queueMethod(item, name, queueName, reject);
taskOptions.taskName = name;
taskOptions.method = item;
return self.queueTask(taskOptions);
}

// Not a queue hash; stop
if (!queueName) {
if (!priority) {
return;
}

self.queueMethod(item, queueName, reject);
self.queueTaskGroup(item, taskOptions);
}

validMethods.forEach(addInQueue);
Expand Down Expand Up @@ -685,7 +770,12 @@ class Generator extends EventEmitter {
}

const instantiate = (Generator, path) => {
Generator.resolved = require.resolve(path);
if (path === 'unknown') {
Generator.resolved = path;
} else {
Generator.resolved = require.resolve(path);
}

Generator.namespace = this.env.namespace(path);

return this.env.instantiate(Generator, {
Expand Down

0 comments on commit 8dac552

Please sign in to comment.