Last active
December 22, 2015 02:59
-
-
Save mnpenner/6407135 to your computer and use it in GitHub Desktop.
Collects and compiles any .js, .css and .less files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var startTime = +new Date(); | |
var FileSystem = require('fs'); | |
var Path = require('path'); | |
var _ = require('underscore'); | |
var UglifyJS = require('uglify-js'); | |
var UglifyCSS = require('uglifycss'); | |
var Less = require('less'); | |
var Set = require('./Set'); | |
var debug = false; | |
var bundleFile = './bundle.json'; | |
process.argv.slice(2).forEach(function(arg) { | |
switch(arg) { | |
case '-d': | |
case '--debug': | |
debug = true; | |
break; | |
default: | |
console.warn("ignoring unrecognized argument '" + arg + "'"); | |
break; | |
} | |
}); | |
function comparePaths(a, b) { | |
if(a === b) return 0; | |
if(!isLocalPath(a) || !isLocalPath(b)) return 0; | |
var p1 = a.split(/[\/\\]+/); | |
var p2 = b.split(/[\/\\]+/); | |
if(p1.length !== p2.length) { | |
return p1.length > p2.length ? -1 : 1; | |
} | |
var main = /(^|[/\\])(main|index)\.\w+$/; | |
if(main.test(a)) { | |
if(!main.test(b)) { | |
return 1; | |
} | |
} else if(main.test(b)) { | |
return -1; | |
} | |
return a < b ? -1 : 1; | |
} | |
function readdirRecursiveSync(baseDir) { | |
var results = []; | |
var list = FileSystem.readdirSync(baseDir); | |
list.forEach(function(filename) { | |
var file = Path.join(baseDir, filename); | |
var stat = FileSystem.statSync(file); | |
if(stat && stat.isDirectory()) results = results.concat(readdirRecursiveSync(file)); | |
else results.push(file); | |
}); | |
return results; | |
} | |
function isLocalPath(path) { | |
return !/^(\w+:)?\/\//.test(path); | |
} | |
Array.prototype.insert = function(index, item) { | |
this.splice(index, 0, item); | |
}; | |
var staticDir = './static'; | |
var staticFiles = new Set(readdirRecursiveSync(staticDir)); | |
var bundle; | |
if(FileSystem.existsSync(bundleFile)) { | |
bundle = require(bundleFile); | |
} else { | |
bundle = { | |
scripts: [], | |
stylesheets: [], | |
ignore: [] | |
}; | |
} | |
// remove files that have been deleted/renamed | |
['stylesheets','scripts'].forEach(function(i) { | |
bundle[i] = bundle[i].filter(function(f) { | |
return !isLocalPath(f) || FileSystem.existsSync(f); | |
}); | |
}); | |
// remove 'min' files if uncompressed version exists | |
staticFiles.each(function(f) { | |
var m = /^(.*)\.min(\.\w+)$/.exec(f); | |
if(m !== null && staticFiles.contains(m[1] + m[2])) { | |
staticFiles.remove(f); | |
} | |
}); | |
// skip files that have already been added | |
['stylesheets','scripts'].forEach(function(i) { | |
bundle[i].forEach(function(f, j) { | |
if(isLocalPath(f)) { | |
staticFiles.remove(Path.relative(__dirname, f)); | |
} | |
}); | |
}); | |
// skip any files that are to be ignored | |
bundle.ignore.forEach(function(patt) { | |
var re = new RegExp(patt); | |
staticFiles.each(function(f) { | |
if(re.test(f)) { | |
staticFiles.remove(f); | |
} | |
}); | |
}); | |
var sortedFiles = staticFiles.toArray().sort(comparePaths); | |
sortedFiles.forEach(function(f) { | |
var i = 0; | |
if(/\.(js|ts|coffee)$/.test(f)) { | |
while(i < bundle.scripts.length && comparePaths(f, bundle.scripts[i]) > 0) ++i; | |
bundle.scripts.insert(i, f); | |
} else if(/\.(css|less|sass|scss)$/.test(f)) { | |
while(i < bundle.stylesheets.length && comparePaths(f, bundle.stylesheets[i]) > 0) ++i; | |
bundle.stylesheets.insert(i, f); | |
} | |
}); | |
FileSystem.writeFileSync(bundleFile, JSON.stringify(bundle, null, 4)); | |
var bundleJsFile = Path.join('public','bundle.js'); | |
var scriptBundle = UglifyJS.minify(bundle.scripts.filter(isLocalPath), { | |
fromString: false, | |
mangle: !debug, | |
outSourceMap: bundleJsFile, | |
output: { | |
beautify: debug | |
} | |
}); | |
FileSystem.writeFile(bundleJsFile, scriptBundle.code); | |
FileSystem.writeFile(bundleJsFile+'.map', scriptBundle.map); | |
var cssSources = []; | |
bundle.stylesheets.forEach(function(filename) { | |
if(isLocalPath(filename)) { | |
var fileContents = FileSystem.readFileSync(filename, {encoding: 'utf8'}); | |
if(/\.css$/.test(filename)) { | |
cssSources.push(fileContents); | |
} else if(/\.less$/.test(filename)) { | |
var parser = new Less.Parser({ | |
paths: [staticDir], | |
filename: filename | |
}); | |
parser.parse(fileContents, function(e, tree) { | |
cssSources.push(tree.toCSS()); | |
}); | |
} | |
} | |
}); | |
var bundleCssFile = Path.join('public','bundle.css'); | |
var cssCode = UglifyCSS.processString(cssSources.join('')); | |
FileSystem.writeFile(bundleCssFile, cssCode); | |
var elapsed = +new Date() - startTime; | |
console.info('done (' + elapsed + ' ms)'); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = function(arr) { | |
var map = {}; | |
this.contains = function(val) { | |
return map.hasOwnProperty(val); | |
}; | |
this.add = function(val) { | |
map[val] = true; | |
}; | |
this.remove = function(val) { | |
delete map[val]; | |
}; | |
this.clear = function() { | |
map = {}; | |
}; | |
this.size = function() { | |
return this.toArray().length; | |
}; | |
this.isEmpty = function() { | |
return this.size() === 0; | |
}; | |
this.toArray = function() { | |
var arr = []; | |
for(var o in map) { | |
if(map.hasOwnProperty(o)) { | |
arr.push(o); | |
} | |
} | |
return arr; | |
}; | |
this.each = function(callback) { | |
for(var o in map) { | |
if(map.hasOwnProperty(o)) { | |
callback(o); | |
} | |
} | |
}; | |
if(arr) { | |
for(var i=0; i<arr.length; ++i) { | |
this.add(arr[i]); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment