Comparing version 0.1.0 to 0.1.1
@@ -6,5 +6,7 @@ var connect = require("connect"), | ||
async = require("async"), | ||
_ = require("underscore"); | ||
_ = require("underscore"), | ||
Minimizers = require("./minimizers"), | ||
Cachers = require("./cachers"); | ||
module.exports = function(name) { | ||
var AirDrop = function(name) { | ||
var package = function(req, res, next) { | ||
@@ -17,5 +19,6 @@ return package.router(req, res, next); | ||
paths: [], | ||
shouldMinimize: false, | ||
minimizer: Minimizers.None, | ||
shouldPackage: false, | ||
shouldCache: false | ||
cacher: null, | ||
explicitlyUseBrowserRequire: false | ||
}); | ||
@@ -25,3 +28,3 @@ | ||
package.router = package.buildRouter(); | ||
package.router = package._buildRouter(); | ||
@@ -33,2 +36,7 @@ package.include("public/require.js"); | ||
module.exports = AirDrop; | ||
AirDrop.Minimizers = Minimizers; | ||
AirDrop.Cachers = Cachers; | ||
var packageMethods = { | ||
@@ -45,4 +53,12 @@ require: function(path) { | ||
minimize: function(bool) { | ||
this.shouldMinimize = (typeof bool === "undefined") ? true : bool; | ||
minimize: function(boolOrMinimizer) { | ||
if(_.isUndefined(boolOrMinimizer) || boolOrMinimizer === true) { | ||
this.minimizer = Minimizers.Default; | ||
} | ||
else if(!boolOrMinimizer) { | ||
this.minimizer = Minimizers.None; | ||
} | ||
else if(_.isFunction(boolOrMinimizer)) { | ||
this.minimizer = boolOrMinimizer; | ||
} | ||
return this; | ||
@@ -56,78 +72,97 @@ }, | ||
cache: function(bool) { | ||
this.shouldCache = (typeof bool === "undefined") ? true: bool; | ||
cache: function(boolOrCacher) { | ||
if(_.isUndefined(boolOrCacher) || boolOrCacher === true) { | ||
this.cacher = Cachers.Default; | ||
} | ||
else if(!boolOrCacher) { | ||
this.cacher = Cachers.None; | ||
} | ||
else if(_.isFunction(boolOrCacher)) { | ||
this.cacher = boolOrCacher; | ||
} | ||
return this; | ||
}, | ||
buildRouter: function() { | ||
var package = this; | ||
useCachedResult: function cache(key, fetchFunc, cb) { | ||
this.cacher ? this.cacher(key, _.bind(fetchFunc, this), cb) : fetchFunc(cb); | ||
}, | ||
function cache(origFunc) { | ||
var cachedData, fakeResponse, cachedHeaders = []; | ||
useBrowserRequire: function(bool) { | ||
this.explicitlyUseBrowserRequire = (typeof bool === "undefined") ? true : bool; | ||
return this; | ||
}, | ||
fakeResponse = function(origRes) { | ||
return { | ||
setHeader: function(k,v) { cachedHeaders.push([k,v]); origRes.setHeader(k,v); }, | ||
write: function(data) { cachedData = data; origRes.write(data); }, | ||
end: function(){ origRes.end() } | ||
}; | ||
}; | ||
_shouldUseBrowserRequire: function() { | ||
if(typeof this.explicitlyUseBrowserRequire !== "undefined") { | ||
return this.explicitlyUseBrowserRequire; | ||
} | ||
var implicitUse = _(this.paths).any(function(tuple) { | ||
return tuple[0] === "require"; | ||
}); | ||
return implicitUse; | ||
}, | ||
return function(req, res) { | ||
if(!package.shouldCache) { | ||
cachedData = null; | ||
cachedHeaders = []; | ||
return origFunc(req, res); | ||
} | ||
source: function(cb) { | ||
var package = this; | ||
expandPaths(package.allPaths(), function(err, expandedPaths) { | ||
if(err) { return cb(err) } | ||
orderedAsyncMap(expandedPaths, _.bind(package._fetchCode, package), function(err, parts) { | ||
if(err) { return cb(err) } | ||
cb(null, package._applyMinimization(parts.join("\n"))); | ||
}); | ||
}); | ||
}, | ||
if(cachedData) { | ||
for(var i=0; i < cachedHeaders.length; i++) { | ||
var tuple = cachedHeaders[i]; | ||
res.setHeader(tuple[0], tuple[1]); | ||
} | ||
res.write(cachedData); | ||
res.end(); | ||
} else { | ||
origFunc(req, fakeResponse(res)); | ||
} | ||
} | ||
allPaths: function() { | ||
var all = this.paths; | ||
if(this._shouldUseBrowserRequire()) { | ||
all = [["include", __dirname + "/browser-require.js"]].concat(all); | ||
} | ||
return all; | ||
}, | ||
_buildRouter: function() { | ||
var package = this; | ||
return connect.router(function(app) { | ||
app.get("/air-drop/" + package.packageName + ".js", cache(function(req, res) { | ||
expandPaths(package.paths, function(err, expandedPaths) { | ||
if(err) { throw err } | ||
async.map(expandedPaths, _.bind(package.fetchCode, package), function(err, parts) { | ||
res.setHeader("Content-Type", "application/javascript"); | ||
res.write(package.minimize(parts.join("\n"))); | ||
res.end(); | ||
}); | ||
}); | ||
})); | ||
app.get("/air-drop/" + package.packageName + "/include/:filepath", cache(function(req, res) { | ||
var filepath = req.params.filepath.replace(/\|/g, "/"); | ||
readWrapFile("include", filepath, function(err, parts) { | ||
function deliverSource(req, res) { | ||
return function(err, data) { | ||
if(err) throw err; | ||
res.setHeader("Content-Type", "application/javascript"); | ||
res.write(parts); | ||
res.write(data); | ||
res.end(); | ||
}); | ||
})); | ||
app.get("/air-drop/" + package.packageName + "/require/:filepath", cache(function(req, res) { | ||
var filepath = req.params.filepath.replace(/\|/g, "/"); | ||
readWrapFile("require", filepath, function(err, parts) { | ||
res.setHeader("Content-Type", "application/javascript"); | ||
res.write(parts); | ||
res.end(); | ||
}); | ||
})); | ||
}; | ||
} | ||
app.get("/air-drop/" + package.packageName + ".js", function(req, res) { | ||
package.useCachedResult(package.packageName, package.source, deliverSource(req, res)); | ||
}); | ||
app.get("/air-drop/" + package.packageName + "/include/:filepath", function(req, res) { | ||
var filepath = req.params.filepath.replace(/\|/g, "/"), | ||
key = package.packageName + "/include/" + filepath, | ||
fetchFunc = function(cb) { | ||
readWrapFile("include", filepath, cb); | ||
}; | ||
package.useCachedResult(key, fetchFunc, deliverSource(req, res)); | ||
}); | ||
app.get("/air-drop/" + package.packageName + "/require/:filepath", function(req, res) { | ||
var filepath = req.params.filepath.replace(/\|/g, "/"), | ||
key = package.packageName + "/require/" + filepath, | ||
fetchFunc = function(cb) { | ||
readWrapFile("require", filepath, cb); | ||
}; | ||
package.useCachedResult(key, fetchFunc, deliverSource(req, res)); | ||
}); | ||
}); | ||
}, | ||
fetchCode: function(tuple, cb) { | ||
_fetchCode: function(tuple, cb) { | ||
var wrapper = tuple[0], | ||
globPath = tuple[1], | ||
wrap = this.fetchWrappedFile(wrapper); | ||
wrap = this._fetchWrappedFile(wrapper); | ||
glob(globPath, function(err, filePaths) { | ||
if(err) { return cb(err); } | ||
async.map(filePaths || [], wrap, function(err, parts) { | ||
orderedAsyncMap(filePaths || [], wrap, function(err, parts) { | ||
err ? cb(err) : cb(null, parts.join("\n")) | ||
@@ -138,3 +173,3 @@ }); | ||
fetchWrappedFile: function(wrapper) { | ||
_fetchWrappedFile: function(wrapper) { | ||
var package = this; | ||
@@ -149,3 +184,3 @@ if(package.shouldPackage) { | ||
path = path.replace(/\//g, "|"); | ||
cb(null, "document.write('<scr'+'ipt src=\"/air-drop/" + package.packageName + "/" + wrapper + "/" + path + "\" type=\"text/javascript\"></scr'+'ipt>');\n"); | ||
cb(null, "document.write('<scr'+'ipt src=\"/air-drop/" + package.packageName + "/" + wrapper + "/" + path + "\" type=\"text/javascript\"></scr'+'ipt>');"); | ||
} | ||
@@ -155,12 +190,5 @@ } | ||
minimize: function(code) { | ||
if(!this.shouldMinimize) { return code; } | ||
var jsp = require("uglify-js").parser; | ||
var pro = require("uglify-js").uglify; | ||
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 | ||
return pro.gen_code(ast); // compressed code here | ||
_applyMinimization: function(code) { | ||
if(this.minimizer) { return this.minimizer(code); } | ||
return code; | ||
} | ||
@@ -180,6 +208,6 @@ }; | ||
if(wrapper === "require") { | ||
cb(null, "// " + wrapper + " " + path + "\nrequire.define('" + moduleName(path) + "', function(require, module, exports){\n" + data + "\n});\n"); | ||
cb(null, "require.define('" + moduleName(path) + "', function(require, module, exports){\n" + data + "\n});\n"); | ||
} | ||
else { | ||
cb(null, "// " + wrapper + " " + path + "\n" + data + "\n"); | ||
cb(null, data + "\n"); | ||
} | ||
@@ -191,3 +219,3 @@ }); | ||
function expandPaths(paths, cb) { | ||
async.concat(paths, expandPath, function(err, allPaths) { | ||
orderedAsyncConcat(paths, expandPath, function(err, allPaths) { | ||
if(err) { return cb(err); } | ||
@@ -217,1 +245,33 @@ var flattened = allPaths, | ||
function orderedAsyncMap(arr, iterator, origCb) { | ||
var arrWithIndexes = _(arr).map(function(item, i) { return [item, i]; }); | ||
var wrappedIterator = function(tuple, cb) { | ||
var origItem = tuple[0], | ||
i = tuple[1]; | ||
iterator(origItem, function(err, result) { | ||
cb(err, [result, i]); | ||
}); | ||
}; | ||
async.map(arrWithIndexes, wrappedIterator, function(err, tuples) { | ||
if(err) { return origCb(err); } | ||
var orderedItems = []; | ||
_(tuples).each(function(tuple) { | ||
var item = tuple[0], | ||
i = tuple[1]; | ||
orderedItems[i] = item; | ||
}); | ||
origCb(null, orderedItems); | ||
}); | ||
} | ||
function orderedAsyncConcat(arr, iterator, cb) { | ||
orderedAsyncMap(arr, iterator, function(err, items) { | ||
if(err) { return cb(err); } | ||
var head = items.shift(); | ||
for(var i=0; i < items.length; i++) { | ||
head = head.concat(items[i]); | ||
} | ||
cb(null, head); | ||
}); | ||
} |
{ | ||
"name": "air-drop", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"author": "Chris Powers <chrisjpowers@gmail.com>", | ||
@@ -23,4 +23,6 @@ "description": "Utility for packaging, manipulating and delivering JS source to the browser", | ||
"node": ">=0.4" | ||
}, | ||
"engines": { | ||
"node": "*" | ||
} | ||
} | ||
} |
# AirDrop | ||
[![Build Status](https://secure.travis-ci.org/chrisjpowers/air-drop.png)](http://travis-ci.org/chrisjpowers/air-drop) | ||
`AirDrop` is a Node.js Connect middleware for compiling, concatenating and minimizing | ||
@@ -15,2 +17,9 @@ your JS/Coffee source files and delivering them to the browser on-the-fly. | ||
You can run the specs with `npm` as well: | ||
``` | ||
cd node_modules/air-drop | ||
npm test | ||
``` | ||
## Including JS Files | ||
@@ -140,1 +149,2 @@ | ||
- Improve caching mechanism to integrate storage outside of memory (flat files, memcached) | ||
- Inline documentation |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
36462
22
747
149
8
3