UNPKG

@boost/core

Version:

Robust pipeline for creating dev tools that separate logic into routines and tasks.

195 lines (194 loc) 7.31 kB
"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;