This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+50
View File
@@ -0,0 +1,50 @@
import { TaskManagerOptions, TaskCallback, RendererContract } from '../Contracts';
/**
* Exposes the API to create a group of tasks and run them in sequence
*/
export declare class TaskManager {
private testing;
/**
* Options
*/
private options;
/**
* The renderer to use for rendering tasks. Automatically decided
*/
private renderer;
/**
* A set of created tasks
*/
private tasks;
/**
* State of the tasks manager
*/
state: 'idle' | 'running' | 'succeeded' | 'failed';
/**
* Reference to the error raised by the task callback (if any)
*/
error?: any;
constructor(options?: Partial<TaskManagerOptions>, testing?: boolean);
/**
* Instantiates the tasks renderer
*/
private instantiateRenderer;
/**
* Run a given task. The underlying code assumes that tasks are
* executed in sequence.
*/
private runTask;
/**
* Register a new task
*/
add(title: string, callback: TaskCallback): this;
/**
* Define a custom logging renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer: RendererContract): this;
/**
* Run tasks
*/
run(): Promise<void>;
}
+135
View File
@@ -0,0 +1,135 @@
"use strict";
/*
* @poppinss/cliui
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskManager = void 0;
const index_1 = require("./index");
const Verbose_1 = require("./Renderers/Verbose");
const Minimal_1 = require("./Renderers/Minimal");
/**
* Default set of options
*/
const DEFAULTS = {
colors: true,
interactive: true,
verbose: false,
};
/**
* Exposes the API to create a group of tasks and run them in sequence
*/
class TaskManager {
constructor(options, testing = false) {
this.testing = testing;
/**
* A set of created tasks
*/
this.tasks = [];
/**
* State of the tasks manager
*/
this.state = 'idle';
this.options = { ...DEFAULTS, ...options };
this.instantiateRenderer();
}
/**
* Instantiates the tasks renderer
*/
instantiateRenderer() {
const rendererOptions = {
colors: this.options.colors,
interactive: this.options.interactive,
};
/**
* Using verbose render when verbose option is true or terminal is not
* interactive
*/
if (this.options.verbose || this.testing || !this.options.interactive) {
this.renderer = new Verbose_1.VerboseRenderer(rendererOptions, this.testing);
return;
}
/**
* Otheriwse using the minimal renderer
*/
this.renderer = new Minimal_1.MinimalRenderer(rendererOptions, this.testing);
}
/**
* Run a given task. The underlying code assumes that tasks are
* executed in sequence.
*/
async runTask(index) {
const task = this.tasks[index];
if (!task) {
return;
}
/**
* Start the underlying task
*/
task.task.start();
/**
* Method to invoke when callback has been completed
*/
const complete = async (message) => {
if (task.task.state !== 'running') {
return;
}
task.task.complete(message);
await this.runTask(index + 1);
};
/**
* Method to invoke when callback has been failed
*/
const fail = async (message) => {
if (task.task.state !== 'running') {
return;
}
this.error = message;
this.state = 'failed';
task.task.fail(message);
};
/**
* Invoke callback
*/
try {
await task.callback(this.renderer.logger, { complete, fail });
}
catch (error) {
await fail(error);
}
}
/**
* Register a new task
*/
add(title, callback) {
this.tasks.push({ task: new index_1.Task(title), callback });
return this;
}
/**
* Define a custom logging renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer) {
this.renderer.useRenderer(renderer);
return this;
}
/**
* Run tasks
*/
async run() {
if (this.state !== 'idle') {
return;
}
this.state = 'running';
this.renderer.tasks(this.tasks.map(({ task }) => task)).render();
await this.runTask(0);
if (this.state === 'running') {
this.state = 'succeeded';
}
}
}
exports.TaskManager = TaskManager;
+72
View File
@@ -0,0 +1,72 @@
import { Logger } from '../../Logger';
import { TaskContract, TaskRendererOptions, RendererContract } from '../../Contracts';
/**
* As the name suggests, render tasks in minimal UI for better viewing
* experience.
*/
export declare class MinimalRenderer {
private options;
private testing;
/**
* The renderer to use to output logs
*/
private renderer?;
/**
* List of registered tasks
*/
private registeredTasks;
/**
* Reference to the logger. We will capture logger messages
* and show them next to the task
*/
logger: Logger;
constructor(options: TaskRendererOptions, testing?: boolean);
/**
* Returns the renderer for rendering the messages
*/
private getRenderer;
/**
* Instantiates the logger and defines a custom renderer
* to log messages in context with the currently running
* task
*/
private instantiateLogger;
/**
* Returns the presentation string for an idle task
*/
private presentIdleTask;
/**
* Returns the presentation string for a running task. The log line is
* updated when logger recieves the message.
*/
private presentRunningTask;
/**
* Returns the presentation string for a failed task
*/
private presentFailedTask;
/**
* Returns the presentation string for a succeeded task
*/
private presentSucceededTask;
/**
* Renders a given task
*/
private renderTask;
/**
* Re-renders all tasks by inspecting their current state
*/
private renderTasks;
/**
* Define a custom renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer: RendererContract): this;
/**
* Register tasks to render
*/
tasks(tasks: TaskContract[]): this;
/**
* Render all tasks
*/
render(): void;
}
+143
View File
@@ -0,0 +1,143 @@
"use strict";
/*
* @poppinss/cliui
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MinimalRenderer = void 0;
const Icons_1 = require("../../Icons");
const Logger_1 = require("../../Logger");
const Console_1 = require("../../Renderer/Console");
/**
* As the name suggests, render tasks in minimal UI for better viewing
* experience.
*/
class MinimalRenderer {
constructor(options, testing = false) {
this.options = options;
this.testing = testing;
}
/**
* Returns the renderer for rendering the messages
*/
getRenderer() {
if (!this.renderer) {
this.renderer = new Console_1.ConsoleRenderer();
}
return this.renderer;
}
/**
* Instantiates the logger and defines a custom renderer
* to log messages in context with the currently running
* task
*/
instantiateLogger() {
/**
* The minimal renderer must always be used when term
* has support for colors and is tty
*/
this.logger = new Logger_1.Logger({ ...this.options, dim: true }, this.testing);
this.logger.useRenderer({
log: (message) => this.renderTasks(message),
logError: (message) => this.renderTasks(message),
logUpdate: (message) => this.renderTasks(message),
logUpdateDone: () => { },
});
}
/**
* Returns the presentation string for an idle task
*/
presentIdleTask(task) {
return `${this.logger.colors.dim(Icons_1.icons.pointer)} ${this.logger.colors.dim(task.title)}`;
}
/**
* Returns the presentation string for a running task. The log line is
* updated when logger recieves the message.
*/
presentRunningTask(task, logLine) {
let message = `${Icons_1.icons.pointer} ${task.title}`;
if (!logLine) {
return message;
}
const lines = logLine.trim().split('\n');
return `${message}\n ${lines[0]}`;
}
/**
* Returns the presentation string for a failed task
*/
presentFailedTask(task) {
const pointer = this.logger.colors.red(Icons_1.icons.pointer);
const duration = this.logger.colors.dim(task.duration);
let message = `${pointer} ${task.title} ${duration}`;
if (!task.completionMessage) {
return message;
}
const errorMessage = typeof task.completionMessage === 'string'
? task.completionMessage
: task.completionMessage.message;
message = `${message}\n ${this.logger.colors.red(errorMessage)}`;
return message;
}
/**
* Returns the presentation string for a succeeded task
*/
presentSucceededTask(task) {
const pointer = this.logger.colors.green(Icons_1.icons.pointer);
const duration = this.logger.colors.dim(task.duration);
let message = `${pointer} ${task.title} ${duration}`;
if (!task.completionMessage) {
return message;
}
message = `${message}\n ${this.logger.colors.dim(task.completionMessage)}`;
return message;
}
/**
* Renders a given task
*/
renderTask(task, logLine) {
switch (task.state) {
case 'idle':
return this.presentIdleTask(task);
case 'running':
return this.presentRunningTask(task, logLine);
case 'succeeded':
return this.presentSucceededTask(task);
case 'failed':
return this.presentFailedTask(task);
}
}
/**
* Re-renders all tasks by inspecting their current state
*/
renderTasks(logLine) {
this.getRenderer().logUpdate(this.registeredTasks.map((task) => this.renderTask(task, logLine)).join('\n'));
}
/**
* Define a custom renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer) {
this.renderer = renderer;
return this;
}
/**
* Register tasks to render
*/
tasks(tasks) {
this.registeredTasks = tasks;
return this;
}
/**
* Render all tasks
*/
render() {
this.instantiateLogger();
this.registeredTasks.forEach((task) => task.onUpdate(() => this.renderTasks()));
this.renderTasks();
}
}
exports.MinimalRenderer = MinimalRenderer;
+55
View File
@@ -0,0 +1,55 @@
import { Logger } from '../../Logger';
import { TaskContract, TaskRendererOptions, RendererContract } from '../../Contracts';
/**
* Verbose renderer shows a detailed output of the tasks and the
* messages logged by a given task
*/
export declare class VerboseRenderer {
private options;
private testing;
/**
* The renderer to use to output logs
*/
private renderer?;
/**
* List of registered tasks
*/
private registeredTasks;
/**
* Reference to the logger. We will capture logger messages
* and show them next to the task
*/
logger: Logger;
constructor(options: TaskRendererOptions, testing?: boolean);
/**
* Returns the renderer for rendering the messages
*/
private getRenderer;
/**
* Prefixes pipe to a line of text
*/
private prefixPipe;
/**
* Instantiates the logger and defines a custom renderer
* to log messages in context with the currently running
* task
*/
private instantiateLogger;
/**
* Logs message based upon the state of the task
*/
private updateTask;
/**
* Define a custom renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer: RendererContract): this;
/**
* Register tasks to render
*/
tasks(tasks: TaskContract[]): this;
/**
* Render all tasks
*/
render(): void;
}
+108
View File
@@ -0,0 +1,108 @@
"use strict";
/*
* @poppinss/cliui
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.VerboseRenderer = void 0;
const Logger_1 = require("../../Logger");
const Console_1 = require("../../Renderer/Console");
/**
* Verbose renderer shows a detailed output of the tasks and the
* messages logged by a given task
*/
class VerboseRenderer {
constructor(options, testing = false) {
this.options = options;
this.testing = testing;
}
/**
* Returns the renderer for rendering the messages
*/
getRenderer() {
if (!this.renderer) {
this.renderer = new Console_1.ConsoleRenderer();
}
return this.renderer;
}
/**
* Prefixes pipe to a line of text
*/
prefixPipe(text) {
return text
.split('\n')
.map((line) => `${this.logger.colors.dim('│')} ${line}`)
.join('\n');
}
/**
* Instantiates the logger and defines a custom renderer
* to log messages in context with the currently running
* task
*/
instantiateLogger() {
this.logger = new Logger_1.Logger({ ...this.options, dim: true }, this.testing);
this.logger.useRenderer({
log: (message) => this.getRenderer().log(this.prefixPipe(message)),
logError: (message) => this.getRenderer().logError(this.prefixPipe(message)),
logUpdate: (message) => this.getRenderer().logUpdate(this.prefixPipe(message)),
logUpdateDone: () => this.getRenderer().logUpdateDone(),
});
}
/**
* Logs message based upon the state of the task
*/
updateTask(task) {
/**
* Task started running
*/
if (task.state === 'running') {
this.getRenderer().log(`${this.logger.colors.dim('┌')} ${task.title}`);
return;
}
const pipe = this.logger.colors.dim('└');
const duration = this.logger.colors.dim(`(${task.duration})`);
/**
* Task failed
*/
if (task.state === 'failed') {
task.completionMessage && this.logger.fatal(task.completionMessage);
this.getRenderer().logError(`${pipe} ${this.logger.colors.red('failed')} ${duration}`);
return;
}
/**
* Task succeeded
*/
if (task.state === 'succeeded') {
task.completionMessage && this.logger.colors.green(task.completionMessage);
this.getRenderer().log(`${pipe} ${this.logger.colors.green('completed')} ${duration}`);
return;
}
}
/**
* Define a custom renderer. Logs to "stdout" and "stderr"
* by default
*/
useRenderer(renderer) {
this.renderer = renderer;
return this;
}
/**
* Register tasks to render
*/
tasks(tasks) {
this.registeredTasks = tasks;
return this;
}
/**
* Render all tasks
*/
render() {
this.instantiateLogger();
this.registeredTasks.forEach((task) => task.onUpdate(($task) => this.updateTask($task)));
}
}
exports.VerboseRenderer = VerboseRenderer;
+43
View File
@@ -0,0 +1,43 @@
import { TaskContract, UpdateListener } from '../Contracts';
/**
* Task exposes a very simple API to create tasks with states, along with a
* listener to listen for the task state updates.
*
* The task itself has does not render anything to the console. The task
* renderers does that.
*/
export declare class Task implements TaskContract {
title: string;
private startTime;
private onUpdateListener;
/**
* Duration of the task. Updated after the task is over
*/
duration?: string;
/**
* Message set after completing the task. Can be an error or the
* a success message
*/
completionMessage?: TaskContract['completionMessage'];
/**
* Task current state
*/
state: TaskContract['state'];
constructor(title: string);
/**
* Bind a listener to listen to the state updates of the task
*/
onUpdate(listener: UpdateListener): this;
/**
* Start the task
*/
start(): this;
/**
* Mark task as completed
*/
complete(message?: string): this;
/**
* Mark task as failed
*/
fail(error: TaskContract['completionMessage']): this;
}
+69
View File
@@ -0,0 +1,69 @@
"use strict";
/*
* @poppinss/cliui
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Task = void 0;
const pretty_hrtime_1 = __importDefault(require("pretty-hrtime"));
/**
* Task exposes a very simple API to create tasks with states, along with a
* listener to listen for the task state updates.
*
* The task itself has does not render anything to the console. The task
* renderers does that.
*/
class Task {
constructor(title) {
this.title = title;
this.onUpdateListener = () => { };
/**
* Task current state
*/
this.state = 'idle';
}
/**
* Bind a listener to listen to the state updates of the task
*/
onUpdate(listener) {
this.onUpdateListener = listener;
return this;
}
/**
* Start the task
*/
start() {
this.state = 'running';
this.startTime = process.hrtime();
this.onUpdateListener && this.onUpdateListener(this);
return this;
}
/**
* Mark task as completed
*/
complete(message) {
this.state = 'succeeded';
this.duration = (0, pretty_hrtime_1.default)(process.hrtime(this.startTime));
this.completionMessage = message;
this.onUpdateListener && this.onUpdateListener(this);
return this;
}
/**
* Mark task as failed
*/
fail(error) {
this.state = 'failed';
this.duration = (0, pretty_hrtime_1.default)(process.hrtime(this.startTime));
this.completionMessage = error;
this.onUpdateListener && this.onUpdateListener(this);
return this;
}
}
exports.Task = Task;