var fs = require('fs');
var pathHelper = require('path');
var common = require('../common/common.js');
var errMsg = common.messages.error;
var successMsg = common.messages.success;
var Validator = require('../validator/ADCValidator.js').Validator;
/**
* Validate and compress the ADC directory to an `.adc` file
*
* @class ADC.Builder
* @private
*/
function Builder(adcDirPath) {
/**
* Root dir of the current ADCUtil
*/
this.rootdir = pathHelper.resolve(__dirname, "../../");
/**
* Name of the ADC
* @type {string}
*/
this.adcName = '';
/**
* Path to the ADC directory
* @type {string}
*/
this.adcDirectoryPath = adcDirPath ? pathHelper.normalize(adcDirPath) : process.cwd();
/**
* Bin path of the ADC
* @type {string}
*/
this.binPath = '';
/**
* Path of the output file
* @type {string}
*/
this.outputPath = '';
/**
* Sequence of calls
* @type {*|Sequence}
*/
this.sequence = new common.Sequence([
this.createBinDir,
this.compressADC
], this.done, this);
/**
* Report of the validation
*
* @type {{startTime: null, endTime: null, runs: number, total: number, success: number, warnings: number, errors: number}}
*/
this.validationReport = null;
/**
* Logger to override with an object
* @type {{writeMessage : function, writeSuccess : function, writeWarning: function, writeError : function}}
*/
this.logger = null;
}
/**
* Create a new instance of ADC Builder
*
* @constructor
* @param {String} adcDirPath Path of the ADC directory
*/
Builder.prototype.constructor = Builder;
/**
* Build the ADC
*
* @param {Object} [options] Options of validation
* @param {Boolean} [options.test=true] Run unit tests
* @param {Boolean} [options.autoTest=true] Run auto unit tests
* @param {Boolean} [options.xml=true] Validate the config.xml file
* @param {InteractiveADXShell} [options.adxShell] Interactive ADXShell process
* @param {Object} [options.logger] Logger
* @param {Function} [options.writeMessage] Function where regular messages will be print
* @param {Function} [options.writeSuccess] Function where success messages will be print
* @param {Function} [options.writeWarning] Function where warning messages will be print
* @param {Function} [options.writeError] Function where error messages will be print
* @param {Function} [callback] Callback function
* @param {Error} [callback.err] Error
* @param {String} [callback.outputPath] Path of the output
* @param {Object} [callback.report] Validation report
*/
Builder.prototype.build = function build(options, callback) {
// Swap the options
if (typeof options === 'function') {
callback = options;
options = null;
}
this.buildCallback = callback;
this.validator = new Validator(this.adcDirectoryPath);
var self = this;
options = options || {};
options.xml = true;
options.autoTest = true;
if (options.logger) {
this.logger = options.logger;
}
this.validator.validate(options, function validateCallback(err, report) {
if (err) {
return self.sequence.resume(new Error(errMsg.validationFailed));
}
self.adcName = self.validator.adcName;
self.binPath = pathHelper.join(self.adcDirectoryPath, common.ADC_BIN_PATH);
self.validationReport = report;
return self.sequence.resume();
});
};
/**
* Write an error output in the console or in the logger
* @param {String} text Text to write in the console
*/
Builder.prototype.writeError = function writeError(text) {
if (this.logger && typeof this.logger.writeError === 'function') {
this.logger.writeError.apply(this.logger, arguments);
} else {
common.writeError.apply(common, arguments);
}
};
/**
* Write a warning output in the console or in the logger
* @param {String} text Text to write in the console
*/
Builder.prototype.writeWarning = function writeWarning(text) {
if (this.logger && typeof this.logger.writeWarning === 'function') {
this.logger.writeWarning.apply(this.logger, arguments);
} else {
common.writeWarning.apply(common, arguments);
}
};
/**
* Write a success output in the console or in the logger
* @param {String} text Text to write in the console
*/
Builder.prototype.writeSuccess = function writeSuccess(text) {
if (this.logger && typeof this.logger.writeSuccess === 'function') {
this.logger.writeSuccess.apply(this.logger, arguments);
} else {
common.writeSuccess.apply(common, arguments);
}
};
/**
* Write an arbitrary message in the console or in the logger without specific prefix or in the logger
* @param {String} text Text to write in the console
*/
Builder.prototype.writeMessage = function writeMessage(text) {
if (this.logger && typeof this.logger.writeMessage === 'function') {
this.logger.writeMessage.apply(this.logger, arguments);
} else {
common.writeMessage.apply(common, arguments);
}
};
/**
* End of the sequence chain
* @param {Error} err Error
*/
Builder.prototype.done = function done(err) {
if (err) {
this.writeError(err.message);
if (typeof this.buildCallback === 'function') {
this.buildCallback(err, this.outputPath, this.validationReport);
}
return;
}
var output = pathHelper.join(this.binPath, this.adcName + '.adc');
if (!this.validationReport.warnings) {
this.writeSuccess(successMsg.buildSucceed, output);
} else {
this.writeSuccess(successMsg.buildSucceedWithWarning, this.validationReport.warnings, output);
}
if (typeof this.buildCallback === 'function') {
this.buildCallback(err, this.outputPath, this.validationReport);
}
};
/**
* Create a bin directory
*/
Builder.prototype.createBinDir = function createBinDir() {
var self = this;
common.dirExists(this.binPath, function binPathExist(err, exist) {
if (!exist || err) {
var er = fs.mkdirSync(self.binPath);
if (er) {
return self.sequence.resume(er);
}
}
return self.sequence.resume();
});
};
/**
* Compress the ADC directory
*/
Builder.prototype.compressADC = function compressADC() {
var self = this;
common.getDirStructure(self.adcDirectoryPath, function callbackGetStructure(err, structure) {
if (err) {
return self.sequence.resume(err);
}
var zip = common.getNewZip(),
zipDir = '';
structure.forEach(function appendInZip(file) {
var prevDir,
folderLower,
zipDirLower = zipDir.toLowerCase();
if (typeof file === 'string') { // File
if (zipDirLower === 'resources/') return; // Exclude extra files
if (zipDirLower === '' && !/^(config\.xml|readme|changelog)/i.test(file)) return; // Exclude extra files
if (common.isIgnoreFile(file)) return; // Ignore files
zip.file(pathHelper.join(zipDir, file), fs.readFileSync(pathHelper.join(self.adcDirectoryPath, zipDir, file)));
} else { // Directory
if (!file.sub || !file.sub.length) return; // Exclude empty folder
folderLower = file.name.toLowerCase();
if (folderLower === 'bin') return; // Exclude the bin folder
if (folderLower === 'tests') return; // Exclude tests folder
if (zipDirLower === 'resources/' && !/^(dynamic|static|share)$/i.test(folderLower)) return; // Exclude extra directories
if (zipDirLower === '' && !/^(resources)$/.test(folderLower)) return; // Exclude extra directories
prevDir = zipDir;
zipDir += file.name + '/';
zip.folder(zipDir);
file.sub.forEach(appendInZip);
zipDir = prevDir;
}
});
var buffer = zip.generate({type:"nodebuffer"});
self.outputPath = pathHelper.join(self.binPath, self.adcName + '.adc');
fs.writeFile(self.outputPath, buffer, function writeZipFile(err) {
if (err) {
throw err;
}
});
self.sequence.resume();
});
};
// Export the Builder object
exports.Builder = Builder;
/*
* Build the ADC file
*
* @param {Command} program Commander object which hold the arguments pass to the program
* @param {String} path Path of the ADC to directory
*/
exports.build = function build(program, path) {
var builder = new Builder(path);
builder.build(program);
};