symfony-style-console
Version:
Use the style and utilities of the Symfony Console in Node.js
246 lines (245 loc) • 8.06 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var OutputFormatterStyle_1 = require("./OutputFormatterStyle");
var OutputFormatterStyleStack_1 = require("./OutputFormatterStyleStack");
var Helper_1 = require("../Helper/Helper");
/**
* Formatter class for console output.
*
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* Original PHP class
*
* @author Florian Reuschel <florian@loilo.de>
*
* Port to TypeScript
*/
var OutputFormatter = /** @class */ (function () {
/**
* Initializes console output formatter.
*
* @param decorated Whether this formatter should actually decorate strings
* @param styles Object mappping names to [[OutputFormatterStyleInterface]] instances
*/
function OutputFormatter(decorated, styles) {
if (decorated === void 0) { decorated = false; }
if (styles === void 0) { styles = {}; }
/**
* A mapping of formatter style names to their respective classes.
*/
this.styles = {};
this.decorated = !!decorated;
this.setStyle('error', new OutputFormatterStyle_1.default('white', 'red'));
this.setStyle('info', new OutputFormatterStyle_1.default('green'));
this.setStyle('comment', new OutputFormatterStyle_1.default('yellow'));
this.setStyle('question', new OutputFormatterStyle_1.default('black', 'cyan'));
for (var name_1 in styles) {
this.setStyle(name_1, styles[name_1]);
}
this.styleStack = new OutputFormatterStyleStack_1.default();
}
/**
* Escapes the "<" special char in given text.
*
* @param text Text to escape
* @return Escaped text
*/
OutputFormatter.escape = function (text) {
text = text.replace(/([^\\]?)</g, '$1\\<');
return OutputFormatter.escapeTrailingBackslash(text);
};
/**
* Escapes trailing backslash "\" in given text.
*
* @param text Text to escape
* @return Escaped text
*
* @internal
*/
OutputFormatter.escapeTrailingBackslash = function (text) {
if ('\\' === text.slice(-1)) {
var len = text.length;
text = Helper_1.trimEnd(text, '\\');
text += '<<'.repeat(len - text.length);
}
return text;
};
/**
* Creates a new instance containing the same `decorated` flag and styles.
*
* @return The cloned [[OutputFormatter]] instance
*/
OutputFormatter.prototype.clone = function () {
return new OutputFormatter(this.isDecorated(), Object.assign({}, this.styles));
};
/**
* Sets the `decorated` flag property.
*
* @param decorated Whether to decorate the messages or not
*/
OutputFormatter.prototype.setDecorated = function (decorated) {
this.decorated = !!decorated;
};
/**
* Gets the `decorated` flag property.
*
* @return True if the output will decorate messages, false otherwise
*/
OutputFormatter.prototype.isDecorated = function () {
return this.decorated;
};
/**
* Sets a new style.
*
* @param name The style name
* @param style The style instance
*/
OutputFormatter.prototype.setStyle = function (name, style) {
this.styles[name.toLowerCase()] = style;
};
/**
* Checks if output formatter has style with specified name.
*
* @param string name
* @return bool
*/
OutputFormatter.prototype.hasStyle = function (name) {
return !!this.styles[name.toLowerCase()];
};
/**
* Gets style options from style with specified name.
*
* @param name
* @return The style
*
* @throws ReferenceError When style isn't defined
*/
OutputFormatter.prototype.getStyle = function (name) {
if (!this.hasStyle(name)) {
throw new ReferenceError("Undefined style: " + name);
}
return this.styles[name.toLowerCase()];
};
/**
* Formats a message according to the given styles.
*
* @param message The message to style
*
* @return The styled message
*/
OutputFormatter.prototype.format = function (message) {
message = String(message);
var offset = 0;
var output = '';
var tagRegex = '[a-z][a-z0-9,_=;-]*';
var tagsRegex = new RegExp("<((" + tagRegex + ")|\\/(" + tagRegex + ")?)>", 'gi');
var match;
while ((match = tagsRegex.exec(message))) {
var text = match[0];
var pos = match.index;
if (0 != pos && '\\' == message[pos - 1]) {
continue;
}
// add the text up to the next tag
output += this.applyCurrentStyle(message.substr(offset, pos - offset));
offset = pos + text.length;
// opening tag?
var open_1 = '/' !== text[1];
var tag = void 0;
if (open_1) {
tag = match[1];
}
else {
tag = match[3] || '';
}
var style = this.createStyleFromString(tag.toLowerCase());
if (!open_1 && !tag) {
// </>
this.styleStack.pop();
}
else if (false === style) {
output += this.applyCurrentStyle(text);
}
else if (open_1) {
this.styleStack.push(style);
}
else {
this.styleStack.pop(style);
}
}
output += this.applyCurrentStyle(message.slice(offset));
if (-1 !== output.indexOf('<<')) {
return Helper_1.safeReplace(output, {
'\\<': '<',
'<<': '\\'
});
}
return output.replace(/\\</g, '<');
};
/**
* Gets the used style stack.
*/
OutputFormatter.prototype.getStyleStack = function () {
return this.styleStack;
};
/**
* Tries to create new [[OutputFormatterStyleInterface]] instance from string.
*
* @param str The string to create the formatter style from
* @return `false` if `str` is not format string
*/
OutputFormatter.prototype.createStyleFromString = function (str) {
if (this.styles[str]) {
return this.styles[str];
}
var styleRegex = /([^=]+)=([^;]+)(;|$)/g;
var style = new OutputFormatterStyle_1.default();
var gotMatch = false;
var match;
while ((match = styleRegex.exec(str))) {
gotMatch = true;
if ('fg' == match[1]) {
style.setForeground(match[2]);
}
else if ('bg' == match[1]) {
style.setBackground(match[2]);
}
else if ('options' === match[1]) {
var options = Array.from(match[2].match(/([^,;]+)/g));
for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
var option = options_1[_i];
try {
style.setOption(option);
}
catch (e) {
console.warn("Unknown style options are deprecated since version 3.2 and will be removed in 4.0. Error \"" + e + "\".");
return false;
}
}
}
else {
return false;
}
}
if (gotMatch) {
return style;
}
else {
return false;
}
};
/**
* Applies current style from stack to text, if must be applied.
*
* @param text The text to format
* @return The formatted text
*/
OutputFormatter.prototype.applyCurrentStyle = function (text) {
return this.isDecorated() && text.length > 0
? this.styleStack.getCurrent().apply(text)
: text;
};
return OutputFormatter;
}());
exports.default = OutputFormatter;