alnoda-workspaces/workspaces/base-workspace/Cronicle-0.8.61/bin/build-tools.js
2021-07-30 12:18:29 +00:00

343 lines
11 KiB
JavaScript
Executable file

#!/usr/bin/env node
// Build Tools Module -- included by build.js.
// Copies, symlinks and compresses files into the right locations.
// Can also compact & bundle JS/CSS together for distribution.
// Copyright (c) 2015 Joseph Huckaby, MIT License.
var path = require('path');
var fs = require('fs');
var zlib = require('zlib');
var util = require('util');
var os = require('os');
var cp = require('child_process');
var mkdirp = require('mkdirp');
var async = require('async');
var glob = require('glob');
var UglifyJS = require("uglify-js");
var Tools = require('pixl-tools');
var fileStatSync = function(file) {
// no-throw version of fs.statSync
var stats = null;
try { stats = fs.statSync(file); }
catch (err) { return false; }
return stats;
};
var fileExistsSync = function(file) {
// replacement for fs.existsSync which is being removed
return !!fileStatSync(file);
};
var symlinkFile = exports.symlinkFile = function symlinkFile( old_file, new_file, callback ) {
// create symlink to file
// Note: 'new_file' is the object that will be created on the filesystem
// 'old_file' should already exist, and is the file being pointed to
if (new_file.match(/\/$/)) new_file += path.basename(old_file);
// if target exists and is not a symlink, skip this
try {
var stats = fs.lstatSync(new_file);
if (!stats.isSymbolicLink()) return callback();
}
catch (e) {;}
console.log( "Symlink: " + old_file + " --> " + new_file );
try { fs.unlinkSync( new_file ); }
catch (e) {;}
if (!fileExistsSync( path.dirname(new_file) )) {
mkdirp.sync( path.dirname(new_file) );
}
// fs.symlink takes a STRING (not a file path per se) as the old (existing) file,
// and it needs to be relative from new_file. So we need to resolve some things.
var sym_new = path.resolve( new_file );
var sym_old = path.relative( path.dirname(sym_new), path.resolve(old_file) );
fs.symlink( sym_old, sym_new, callback );
};
var copyFile = exports.copyFile = function copyFile( old_file, new_file, callback ) {
// copy file
if (new_file.match(/\/$/)) new_file += path.basename(old_file);
console.log( "Copy: " + old_file + " --> " + new_file );
try { fs.unlinkSync( new_file ); }
catch (e) {;}
if (!fileExistsSync( path.dirname(new_file) )) {
mkdirp.sync( path.dirname(new_file) );
}
var inp = fs.createReadStream(old_file);
var outp = fs.createWriteStream(new_file);
inp.on('end', callback );
inp.pipe( outp );
};
var copyFiles = exports.copyFiles = function copyFiles( src_spec, dest_dir, callback ) {
// copy multiple files to destination directory using filesystem globbing
dest_dir = dest_dir.replace(/\/$/, '');
glob(src_spec, {}, function (err, files) {
// got files
if (files && files.length) {
async.eachSeries( files, function(src_file, callback) {
// foreach file
var stats = fileStatSync(src_file);
if (stats && stats.isFile()) {
copyFile( src_file, dest_dir + '/', callback );
}
else callback();
}, callback );
} // got files
else {
callback( err || new Error("No files found matching: " + src_spec) );
}
} );
};
var compressFile = exports.compressFile = function compressFile( src_file, gz_file, callback ) {
// gzip compress file
console.log( "Compress: " + src_file + " --> " + gz_file );
if (fileExistsSync(gz_file)) {
fs.unlinkSync( gz_file );
}
var gzip = zlib.createGzip();
var inp = fs.createReadStream( src_file );
var outp = fs.createWriteStream( gz_file );
inp.on('end', callback );
inp.pipe(gzip).pipe(outp);
};
var copyCompress = exports.copyCompress = function copyCompress( old_file, new_file, callback ) {
// copy file and create gzip version as well
if (new_file.match(/\/$/)) new_file += path.basename(old_file);
copyFile( old_file, new_file, function(err) {
if (err) return callback(err);
// Make a compressed copy, so node-static will serve it up to browsers
compressFile( old_file, new_file + '.gz', callback );
} );
};
var symlinkCompress = exports.symlinkCompress = function symlinkCompress( old_file, new_file, callback ) {
// symlink file and create gzip version as well
if (new_file.match(/\/$/)) new_file += path.basename(old_file);
symlinkFile( old_file, new_file, function(err) {
if (err) return callback(err);
// Make a compressed copy, so node-static will serve it up to browsers
compressFile( old_file, new_file + '.gz', callback );
} );
};
var deleteFile = exports.deleteFile = function deleteFile( file, callback ) {
// delete file
console.log( "Delete: " + file );
if (fileExistsSync(file)) {
fs.unlink( file, callback );
}
else callback();
};
var deleteFiles = exports.deleteFiles = function deleteFiles( spec, callback ) {
// delete multiple files using filesystem globbing
glob(spec, {}, function (err, files) {
// got files
if (files && files.length) {
async.eachSeries( files, function(file, callback) {
// foreach file
deleteFile( file, callback );
}, callback );
} // got files
else {
callback( err );
}
} );
};
var chmodFiles = exports.chmodFiles = function chmodFiles( mode, spec, callback ) {
// chmod multiple files to specified mode using filesystem globbing
glob(spec, {}, function (err, files) {
// got files
if (files && files.length) {
async.eachSeries( files, function(file, callback) {
// foreach file
fs.chmod( file, mode, callback );
}, callback );
} // got files
else {
callback( err || new Error("No files found matching: " + spec) );
}
} );
};
var copyDir = exports.copyDir = function copyDir( src_dir, dest_dir, exclusive, callback ) {
// recursively copy dir and contents, optionally with exclusive mode
// symlinks are followed, and the target is copied instead
var src_spec = src_dir + '/*';
// exclusive means skip if dest exists (do not replace)
if (exclusive && fileExistsSync(dest_dir)) return callback();
mkdirp.sync( dest_dir );
glob(src_spec, {}, function (err, files) {
// got files
if (files && files.length) {
async.eachSeries( files, function(src_file, callback) {
// foreach file
var stats = fs.statSync(src_file);
if (stats.isFile()) {
copyFile( src_file, dest_dir + '/', callback );
}
else if (stats.isDirectory()) {
copyDir( src_file, dest_dir + '/' + path.basename(src_file), exclusive, callback );
}
}, callback );
} // got files
else {
callback( err );
}
} );
};
var bundleCompress = exports.bundleCompress = function bundleCompress( args, callback ) {
// compress bundle of files
var html_file = args.html_file;
var html_dir = path.dirname( html_file );
if (!fileExistsSync( path.dirname(args.dest_bundle) )) {
mkdirp.sync( path.dirname(args.dest_bundle) );
}
var raw_html = fs.readFileSync( html_file, 'utf8' );
var regexp = new RegExp( "\\<\\!\\-\\-\\s+BUILD\\:\\s*"+args.match_key+"_START\\s*\\-\\-\\>([^]+)\\<\\!\\-\\-\\s*BUILD\\:\\s+"+args.match_key+"_END\\s*\\-\\-\\>" );
if (raw_html.match(regexp)) {
var files_raw = RegExp.$1;
var files = [];
files_raw.replace( /\b(src|href)\=[\"\']([^\"\']+)[\"\']/ig, function(m_all, m_g1, m_g2) {
files.push( path.join(html_dir, m_g2) );
} );
if (files.length) {
console.log("Bundling files: ", files);
var raw_output = '';
// optionally add file header
if (args.header) {
raw_output += args.header.trim() + "\n";
}
if (args.uglify) {
console.log("Running UglifyJS...");
var result = UglifyJS.minify( files );
if (!result || !result.code) return callback( new Error("Failed to bundle script: Uglify failure") );
raw_output += result.code;
}
else {
for (var idx = 0, len = files.length; idx < len; idx++) {
var file = files[idx];
raw_output += fs.readFileSync( file, 'utf8' ) + "\n";
}
}
// optionally strip source map links
// /*# sourceMappingURL=materialdesignicons.min.css.map */
if (args.strip_source_maps) {
raw_output = raw_output.replace(/sourceMappingURL\=\S+/g, "");
}
// write out our bundle
console.log(" --> " + args.dest_bundle);
fs.writeFileSync(args.dest_bundle, raw_output);
// swap a ref link into a copy of the HTML
console.log(" --> " + html_file );
raw_html = raw_html.replace( regexp, args.dest_bundle_tag );
fs.writeFileSync(html_file, raw_html);
// now make a compressed version of the bundle
compressFile( args.dest_bundle, args.dest_bundle + '.gz', function(err) {
if (err) return callback(err);
// and compress the final HTML as well
compressFile( html_file, html_file + '.gz', callback );
});
} // found files
else {
callback( new Error("Could not locate any file references: " + args.src_html + ": " + args.match_key) );
}
}
else {
callback( new Error("Could not locate match in HTML source: " + args.src_html + ": " + args.match_key) );
}
};
var generateSecretKey = exports.generateSecretKey = function generateSecretKey( args, callback ) {
// generate random secret key for a specified JSON config file
// use regular expression to preserve natural file format
var file = args.file;
var key = args.key;
var regex = new RegExp('(\\"'+Tools.escapeRegExp(key)+'\\"\\s*\\:\\s*\\")(.*?)(\\")');
var secret_key = Tools.generateUniqueID(32);
fs.readFile(file, 'utf8', function(err, text) {
if (err) return callback(err);
if (!text.match(regex)) return callback( new Error("Could not locate key to replace: " + file + ": " + key) );
text = text.replace(regex, '$1' + secret_key + '$3');
fs.writeFile( file, text, callback );
});
};
var addToServerStartup = exports.addToServerStartup = function addToServerStartup( args, callback ) {
// add service to init.d
// (RedHat and Ubuntu only -- do nothing on OS X)
var src_file = args.src_file;
var dest_dir = args.dest_dir;
var service_name = args.service_name;
var dest_file = dest_dir + '/' + service_name;
// shell command that activates service on redhat or ubuntu
var cmd = 'chkconfig '+service_name+' on || update-rc.d '+service_name+' defaults';
// skip on os x, and if init dir is missing
if (os.platform() == 'darwin') return callback();
if (!fileExistsSync(dest_dir)) return callback( new Error("Cannot locate init.d directory: " + dest_dir) );
if (process.getuid() != 0) return callback( new Error("Must be root to add services to server startup system.") );
// copy file into place
copyFile( src_file, dest_file, function(err) {
if (err) return callback(err);
// must be executable
fs.chmod( dest_file, "775", function(err) {
if (err) return callback(err);
// exec shell command
cp.exec( cmd, callback );
} );
} );
};
var printMessage = exports.printMessage = function printMessage( args, callback ) {
// output a message to the console
// use process.stdout.write because console.log has been redirected to a file.
process.stdout.write( "\n" + args.lines.join("\n") + "\n\n" );
};