Source: app-wrapper/js/lib/main/mainBase.js
/**
* @fileOverview MainBase class file
* @author Dino Ivankov <dinoivankov@gmail.com>
* @version 1.3.1
*/
const _ = require('lodash');
const util = require('util');
const path = require('path');
const fs = require('fs');
const BaseClass = require('../base').BaseClass;
/**
* Base class for extending when creating other classes
*
* @class
* @memberOf mainScript
* @extends {appWrapper.BaseClass}
* @property {Object} colors Terminal color escape sequences
*/
class MainBaseClass extends BaseClass {
/**
* Creates class instance, setting basic properties, and returning the instance itself
*
* @constructor
* @return {mainScript.MainBaseClass} Instance of current class
*/
constructor () {
super();
this.colors = {
red: '\x1B[1;31m',
green: '\x1B[1;32m',
yellow: '\x1B[1;33m',
gray: '\x1B[0;37m',
reset: '\x1B[0m'
};
this.boundMethods = {
printLog: null,
consoleLog: null
};
return this;
}
/**
* Logs message to console or stdout
*
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error, group, groupCollaped, groupend)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} force Flag to force logging output even if config does not allow it
* @param {Boolean} forceToWindow Flag to force logging to main window console
* @return {undefined}
*/
log (message, type, data, force, forceToWindow) {
if (!type){
type = 'info';
}
let debugEnabled = this.getConfig('main.debug.enabled', false);
if ((type && type.match && !type.match(/(group|delimiter)/)) && (force || debugEnabled)){
if (_.isUndefined(force)){
force = this.forceDebug;
}
let debugLevel = this.getConfig('main.debug.debugLevel', 3);
let debugLevels = this.getConfig('logger.messageLevels');
let typeLevel = debugLevels && debugLevels[type] ? debugLevels[type] : 0;
let doLog = force || false;
if (!doLog && debugEnabled){
if (typeLevel >= debugLevel){
doLog = true;
}
}
if (doLog) {
this._doLog(message, type, data, forceToWindow);
}
}
}
/**
* Logs data to console
*
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error, group, groupCollaped, groupend)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} forceToWindow Flag to force logging to main window console
* @return {undefined}
*/
_doLog (message, type, data, forceToWindow) {
let sourceMessage = message;
if (_.isObject(message) || _.isArray(message)){
message = util.inspect(message, this.inspectOptions);
}
if (message && message.match && message.match(/{(\d+)}/) && _.isArray(data) && data.length) {
message = message.replace(/{(\d+)}/g, (match, number) => {
var index = number - 1;
let dataItem;
if (!_.isUndefined(data[index])){
dataItem = data[index];
if (_.isObject(dataItem) || _.isArray(dataItem)){
dataItem = util.inspect(dataItem, this.inspectOptions);
}
} else {
data[index] = match;
}
return dataItem;
});
}
let originalMessage = message;
if (this.getConfig('main.debug.displayTimestamps')){
message = this.formatTimeNormalize(new Date(), {}, true, false) + ' ' + message;
}
let logMethod = this.boundMethods.printLog;
let clearLastLine = false;
if (mainScript.manifest['chromium-args'] && mainScript.manifest['chromium-args'].match(/--enable-logging=stderr/)){
clearLastLine = true;
logMethod = this.boundMethods.consoleLog;
}
logMethod(message, type);
let mainWindow = this.getMainWindow();
if ((forceToWindow || this.getConfig('main.debug.debugToWindow')) && mainWindow && mainWindow.window && mainWindow.window.getAppWrapper && _.isFunction(mainWindow.window.getAppWrapper) && mainWindow.window.getAppWrapper()){
setTimeout( () => {
if (clearLastLine){
process.stdout.write('\x1B[s');
}
let aw = mainWindow.window.getAppWrapper();
if (_.isObject(sourceMessage) || _.isArray(sourceMessage)){
mainWindow.window.console.log('MAINSCRIPT: ', sourceMessage);
} else {
aw.log('MAINSCRIPT: ' + message, type, data, true);
}
if (clearLastLine){
process.stdout.write('\x1B[u');
process.stdout.write('\x1B[J');
}
}, 0);
}
if (this.getConfig('main.debug.debugToFile')){
let messageObj = {};
if (this.getConfig('main.debug.displayTimestamps')){
messageObj.timestamp = this.formatTimeNormalize(new Date(), {}, true, false);
}
messageObj.className = this.constructor.name;
messageObj.type = type;
messageObj.message = originalMessage;
if (this.getConfig('main.debug.saveStacksToFile')) {
messageObj.stack = this._getStack();
}
let line = '';
if (mainScript.debugToFileStarted){
line += ',\n';
} else {
mainScript.debugToFileStarted = true;
}
line += JSON.stringify(messageObj);
let debugMessageFilePath = this.getDebugMessageFilePath();
fs.writeFileSync(path.resolve(debugMessageFilePath), line, {flag: 'a'});
}
}
/**
* Returns path to debug message log file
*
* @return {string} Path to debug message log file
*/
getDebugMessageFilePath () {
let debugMessageFilePath = path.join(mainScript.getExecPath(), this.getConfig('appConfig.logDir'), this.getConfig('main.debug.debugLogFilename'));
let rotateLogs = this.getConfig('main.debug.rotateLogs');
if (rotateLogs){
debugMessageFilePath += '-' + mainScript.startTime.toISOString().replace(/T.*$/, '');
}
debugMessageFilePath += '.json';
return debugMessageFilePath;
}
/**
* Prints message to stdout
*
* @param {string} message Message to print
* @return {undefined}
*/
print (message){
process.stdout.write(message);
}
/**
* Prints message to stdout with newline appended
*
* @param {string} message Message to print
* @return {undefined}
*/
printLn (message){
process.stdout.write(message.replace(/\r?\n?$/, '\n'));
}
/**
* Prints message to stdout with newline appended
*
* @param {string} message Message to print
* @param {string} type Type of message
* @return {undefined}
*/
printLog (message, type){
if (!type){
type = 'info';
}
if (type == 'info'){
process.stdout.write(this.colors.green);
} else if (type == 'warning'){
process.stdout.write(this.colors.yellow);
} else if (type == 'error'){
process.stdout.write(this.colors.red);
} else {
process.stdout.write(this.colors.gray);
}
process.stdout.write(message.replace(/\r?\n?$/, ''));
process.stdout.write(this.colors.reset + '\n');
}
/**
* Logs message to console
*
* @param {mixed} message Message to log
* @return {undefined}
*/
consoleLog(message){
console.log(message);
}
/**
* Logs to window console
*
* @return {undefined}
*/
windowLog(){
let mw = this.getMainWindow();
if (mw && mw.window && mw.window.console){
mw.window.console.log.apply(mw.window.console, arguments);
} else {
this.log('Can not log to window console!', 'error', []);
}
}
/**
* Returns configuration var value
*
* @param {string} name String representing path to requested var (i.e. 'appConfig.appInfo.name')
* @param {mixed} defaultValue Default value to be returned if configuration var is not found
* @return {mixed} Configuration var value
*/
getConfig (name, defaultValue){
let value = defaultValue;
if (mainScript.config){
value = _.get(mainScript.config, name, defaultValue);
}
return value;
}
/**
* Format time normalized (Y-m-d H:i:s format)
*
* @param {Date} date Date value to format
* @param {Object} options Date format options
* @param {Boolean} includeDate Flag to indicate whether to include date
* @param {Boolean} omitSeconds Flag to indicate whether to omit seconds
* @return {string} Formatted time string
*/
formatTimeNormalize (date, options, includeDate, omitSeconds){
if (_.isString(date)){
date = new Date(date);
}
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
if (month < 10){
month = '0' + month;
}
if (day < 10){
day = '0' + day;
}
if (hours < 10){
hours = '0' + hours;
}
if (minutes < 10){
minutes = '0' + minutes;
}
if (seconds < 10){
seconds = '0' + seconds;
}
var formattedTime = '';
if (includeDate){
formattedTime += year + '-' + month + '-' + day;
}
formattedTime += ' ';
formattedTime += hours;
formattedTime += ':' + minutes;
if (!omitSeconds) {
formattedTime += ':' + seconds;
}
return formattedTime;
}
/**
* Creates directory recursively
*
* @async
* @param {string} directory Absolute directory path
* @param {Number} mode Octal mode definition (i.e. 0o775)
* @return {Boolean} Result of directory creation
*/
async createDirRecursive(directory, mode){
var dirName = path.resolve(directory);
var dirChunks = dirName.split(path.sep);
var dirPath = '';
if (fs.existsSync(dirName)){
if (await this.isFile(dirName)){
this.log('Can\'t create directory "{1}", already exists and it is a file.', 'error', [dirName]);
return false;
}
} else {
dirPath = dirChunks[0];
for(let i=1; i< dirChunks.length;i++){
dirPath = path.join(dirPath, path.sep + dirChunks[i]);
if (!fs.existsSync(dirPath)){
fs.mkdirSync(dirPath, mode);
} else if (await this.isFile(dirPath)){
this.log('Can\'t create directory "{1}", already exists and it is a file.', 'error', [dirPath]);
return false;
}
}
}
return fs.existsSync(dirName);
}
/**
* Creates directory (recursive) and writes file to it
*
* @async
* @param {string} fileName Absolute path to file
* @param {Number} mode Octal mode definition (i.e. 0o775)
* @param {Object} options Options object for fs.writeFileSync
* @param {string} data Data to write to file
* @return {Boolean} True if operation succeeded, false otherwise
*/
async createDirFileRecursive(fileName, mode, options, data){
if (!options){
options = {flag: 'w'};
}
if (!data){
data = '';
}
if (!mode){
mode = 0o755;
}
var filePath = path.resolve(fileName);
var dirName = path.dirname(filePath);
var dirCreated = await this.createDirRecursive(dirName, mode);
if (!dirCreated){
return false;
} else {
try {
fs.writeFileSync(filePath, data, options);
return await this.isFile(filePath);
} catch (ex) {
this.log('Can\'t create file "{1}" - "{2}".', 'error', [filePath, ex && ex.message ? ex.message : ex]);
return false;
}
}
}
/**
* Checks whether given path is a file
*
* @async
* @param {string} file Absolute file path
* @return {Boolean} True if file is file, false otherwise
*/
async isFile(file){
if (!file){
return false;
}
var filePath = path.resolve(file);
var isFile = true;
var exists = fs.existsSync(filePath);
if (exists){
var fileStat = fs.statSync(filePath);
if (!fileStat.isFile()){
isFile = false;
}
} else {
isFile = false;
}
return isFile;
}
/**
* Gets reference to main window if available
*
* @return {(Window|Boolean)} nw.Window or false if not available
*/
getMainWindow () {
let ms = this.getMainScript();
let mw;
if (ms && ms.mainWindow){
mw = ms.mainWindow;
}
if (!mw){
this.log('Can not find mainWindow!', 'error', []);
}
return mw;
}
/**
* Gets appState if available
*
* @return {appWrapper.AppState} appState object
*/
getAppState () {
let appState;
let mainWindow = this.getMainWindow();
if (mainWindow && mainWindow.window && mainWindow.window.appState){
appState = mainWindow.window.appState;
}
if (!appState){
this.log('Can not find appState!', 'error', []);
}
return appState;
}
/**
* Gets appWrapper if available
*
* @return {AppWrapper} Instance of AppWrapper class
*/
getAppWrapper () {
let _appWrapper;
let mainWindow = this.getMainWindow();
if (mainWindow && mainWindow.window && mainWindow.window.getAppWrapper && _.isFunction(mainWindow.window.getAppWrapper)){
_appWrapper = mainWindow.window.getAppWrapper();
}
if (!_appWrapper){
this.log('Can not find appWrapper!', 'error', []);
}
return _appWrapper;
}
/**
* Returns main script object
*
* @return {mainScript.MainScript} mainScript class instance
*/
getMainScript () {
if (mainScript){
return mainScript;
} else {
this.log('Can not find mainScript!', 'error', []);
return false;
}
}
}
exports.MainBaseClass = MainBaseClass;