Skip to content

Instantly share code, notes, and snippets.

@makeusabrew
Created October 25, 2011 11:54
Show Gist options
  • Save makeusabrew/1312457 to your computer and use it in GitHub Desktop.
Save makeusabrew/1312457 to your computer and use it in GitHub Desktop.
Watch a specified directory's JavaScript files and recompile a specified output file if any changes are detected
/**
* Sample usage - note that the trailing slash on the watch directory is important!
*
* node uglify.js /path/to/my/src/directory/ /path/to/my/output/file.js
*/
var jsp = require('uglify-js').parser,
pro = require('uglify-js').uglify,
fs = require('fs');
// take the directory to watch...
var srcDir = process.argv[2];
// ... and the destination file as command line arguments
var destFile = process.argv[3];
// grab the files to watch...
var sourceFiles = fs.readdirSync(srcDir);
// keep a cache of compiled files so we don't have to recompile all of them every time
var compressedFiles = {};
var compile = function(file, cb) {
var code = fs.readFileSync(file, "utf8");
if (compressedFiles[file] == null) {
console.log("compressing "+file);
} else {
console.log("re-compressing "+file);
}
// lifted from https://github.com/mishoo/UglifyJS
var ast = jsp.parse(code); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here
// cache the compressed version of this file
compressedFiles[file] = final_code;
// if we've got a callback, assume it wants the output of all our files
if (typeof cb == 'function') {
var output = "";
for (var i in compressedFiles) {
output += compressedFiles[i]+";";
}
cb(output);
}
}
/**
* Go!
*/
sourceFiles.forEach(function(file) {
if (file.match(/\.js$/)) {
var fullPath = srcDir+file;
compile(fullPath);
fs.watchFile(fullPath, function(curr, prev) {
if (curr.mtime > prev.mtime) {
console.log(file+" modified!");
compile(fullPath, function(output) {
fs.writeFile(destFile, output, function(err) {
if (err) throw err;
console.log("written file to "+destFile);
});
});
} else {
console.log(file+" accessed...");
}
});
}
});
nick@nick-desktop:~/www/foo/node$ node uglify.js ~/www/foo/src/js/ ~/www/foo/public/js/run.js
compressing /home/nick/www/foo/src/js/client.js
compressing /home/nick/www/foo/src/js/bullet.js
compressing /home/nick/www/foo/src/js/surface.js
compressing /home/nick/www/foo/src/js/input.js
compressing /home/nick/www/foo/src/js/message_box.js
client.js accessed...
client.js modified!
re-compressing /home/nick/www/foo/src/js/client.js
written file to /home/nick/www/foo/public/js/run.js
@JamieMason
Copy link

like it!

@makeusabrew
Copy link
Author

Cheers dude - added some sample output to put it in a bit more context! Just a quick and dirty implementation for a project I'm working on - plenty of scope for refinement...

@JamieMason
Copy link

JamieMason commented Oct 25, 2011 via email

@joshnesbitt
Copy link

I did something similar with Ruby the other week, similar output:

#! /usr/bin/ruby
require 'rubygems'
require 'pathname'
require "yui/compressor"

ROOT        = Pathname.new(File.expand_path(".", __FILE__))
OUTPUT_FILE = 'release.js'
INPUT_FILES = [
  "one.js",
  "two.js",
  "three.js"
]

output = ""

INPUT_FILES.each do |file|
  print "."
  output << File.read(ROOT.join(file))
end

compressor = YUI::JavaScriptCompressor.new(:munge => true)

File.open(ROOT.join(OUTPUT_FILE), 'w') {|f| f.write(compressor.compress(output)) }

print "Done!"
puts

@makeusabrew
Copy link
Author

Nice! My ruby's not up to much but I think it looks like the major difference is my cobbled together script watches the file system for any of the relevant files and recompiles on demand. The motivation was because I always wanted to serve compressed JS (even during build) but obviously want to work with the original files.

With this I can just work away and as soon as any of my source files are changed, bam, the main "run.js" file is updated - meaning I can refresh the page I was on to see my changes, albeit served in a single, compressed file.

@joshnesbitt
Copy link

Nice idea. I started something like that for a contracting gig a while back with @JamieMason with testing a bunch of files using Guard. Also wrote Processr which helps manipulate text with filters etc. It's not Node, but I'm tempted by it with the amount of JS I'm writing lately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment