forked from jhuckaby/Cronicle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build-tools.js
executable file
·342 lines (282 loc) · 10.7 KB
/
build-tools.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#!/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" );
};