@boost/core
Version:
Robust pipeline for creating dev tools that separate logic into routines and tasks.
195 lines (194 loc) • 7.31 kB
JavaScript
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const execa_1 = __importDefault(require("execa"));
const split_1 = __importDefault(require("split"));
const optimal_1 = __importStar(require("optimal"));
const common_1 = require("@boost/common");
const debug_1 = require("@boost/debug");
const event_1 = require("@boost/event");
const Task_1 = __importDefault(require("./Task"));
const Parallel_1 = __importDefault(require("./executors/Parallel"));
const Pool_1 = __importDefault(require("./executors/Pool"));
const Serial_1 = __importDefault(require("./executors/Serial"));
const Sync_1 = __importDefault(require("./executors/Sync"));
const wrapWithPromise_1 = __importDefault(require("./helpers/wrapWithPromise"));
const constants_1 = require("./constants");
class Routine extends Task_1.default {
constructor(key, title, options) {
super(title, (context, value) => this.execute(context, value));
this.key = '';
this.parent = null;
// TODO Change to Set in 2.0
this.routines = [];
// TODO Change to Set in 2.0
this.tasks = [];
if (!key || typeof key !== 'string') {
throw new Error('Routine key must be a valid unique string.');
}
this.key = key;
this.options = optimal_1.default(Object.assign({}, options), this.blueprint(optimal_1.predicates), {
name: this.constructor.name,
});
this.debug = debug_1.createDebugger(['routine', this.key]);
this.onCommand = new event_1.Event('command');
this.onCommandData = new event_1.Event('command.data');
}
/**
* Define an optimal blueprint in which to validate and build the
* options passed to the constructor.
*/
blueprint(preds) {
return {};
}
/**
* Called once the routine has been configured and is ready to execute.
*/
bootstrap() { }
/**
* Configure the routine after it has been instantiated.
*/
configure(parent) {
this.debug('Bootstrapping routine');
this.parent = parent;
this.tool = parent.tool;
this.context = parent.context;
this.metadata.depth = parent.metadata.depth + 1;
// Initialize routine
this.bootstrap();
return this;
}
/**
* Execute the current routine and return a new value.
*/
async execute(context, value) {
return this.serializeTasks();
}
/**
* Execute a command with the given arguments and pass the results through a promise.
*/
async executeCommand(command, args, options = {}) {
const { shell, task, wrap } = options, opts = __rest(options, ["shell", "task", "wrap"]);
const stream = shell
? execa_1.default.shell(`${command} ${args.join(' ')}`.trim(), opts)
: execa_1.default(command, args, opts);
this.onCommand.emit([command]);
// Push chunks to the reporter
const unit = task || this;
const handler = (line) => {
if (unit.status === constants_1.STATUS_RUNNING) {
unit.output += line;
// Only capture the status when not empty
if (line) {
unit.statusText = line;
}
this.onCommandData.emit([command, line]);
}
};
if (!shell) {
stream.stdout.pipe(split_1.default()).on('data', handler);
stream.stderr.pipe(split_1.default()).on('data', handler);
}
// Allow consumer to wrap functionality
if (typeof wrap === 'function') {
wrap(stream);
}
return wrapWithPromise_1.default(stream);
}
/**
* Execute routines in parallel.
*/
async parallelizeRoutines(value, routines) {
return new Parallel_1.default(this.tool, this.context).runRoutines(Array.from(routines || this.routines), value);
}
/**
* Execute tasks in parallel.
*/
async parallelizeTasks(value, tasks) {
return new Parallel_1.default(this.tool, this.context).runTasks(Array.from(tasks || this.tasks), value);
}
/**
* Add a new routine within this routine.
*/
pipe(routine) {
if (common_1.instanceOf(routine, Routine)) {
// eslint-disable-next-line no-param-reassign
routine.metadata.index = this.routines.length;
this.routines.push(routine.configure(this));
}
else {
throw new TypeError(this.tool.msg('errors:routineInstanceInvalid'));
}
return this;
}
/**
* Execute routines in a pool.
*/
async poolRoutines(value, options, routines) {
return new Pool_1.default(this.tool, this.context, options).runRoutines(Array.from(routines || this.routines), value);
}
/**
* Execute tasks in a pool.
*/
async poolTasks(value, options, tasks) {
return new Pool_1.default(this.tool, this.context, options).runTasks(Array.from(tasks || this.tasks), value);
}
/**
* Execute routines in sequential (serial) order.
*/
serializeRoutines(value, routines) {
return new Serial_1.default(this.tool, this.context).runRoutines(Array.from(routines || this.routines), value);
}
/**
* Execute tasks in sequential (serial) order.
*/
serializeTasks(value, tasks) {
return new Serial_1.default(this.tool, this.context).runTasks(Array.from(tasks || this.tasks), value);
}
/**
* Execute routines in sync.
*/
async synchronizeRoutines(value, routines) {
return new Sync_1.default(this.tool, this.context).runRoutines(Array.from(routines || this.routines), value);
}
/**
* Execute tasks in sync.
*/
async synchronizeTasks(value, tasks) {
return new Sync_1.default(this.tool, this.context).runTasks(Array.from(tasks || this.tasks), value);
}
/**
* Define an individual task.
*/
task(title, action, scope) {
if (typeof action !== 'function') {
throw new TypeError(this.tool.msg('errors:taskRequireAction'));
}
const task = new Task_1.default(title, action.bind(scope || this));
task.parent = this;
task.metadata.index = this.tasks.length;
this.tasks.push(task);
return task;
}
}
exports.default = Routine;