combohandler
Advanced tools
Comparing version 0.3.6 to 0.3.7
Combo Handler History | ||
===================== | ||
0.3.7 (2013-10-22) | ||
------------------ | ||
* Fixed a few `dynamicPath` middleware edge cases with multiple and duplicate | ||
route parameters. | ||
* Added --no-resolve-symlinks option that uses fs.statSync to validate rootPath | ||
instead of fs.realpathSync. Caveat emptor. | ||
* Updated jshint devDependency to 2.3.0. | ||
* Updated should devDependency to 2.0.2. | ||
0.3.6 (2013-10-18) | ||
@@ -5,0 +18,0 @@ ------------------ |
@@ -17,2 +17,3 @@ /** | ||
"restart": Boolean, | ||
"resolveSymlinks": Boolean, | ||
"root": [String, Array], | ||
@@ -30,2 +31,3 @@ "rootsFile": path, | ||
var shortHands = { | ||
"--no-resolve-symlinks": ["--no-resolveSymlinks"], | ||
"h": ["--help"], | ||
@@ -71,2 +73,5 @@ "v": ["--version"], | ||
msg.push(""); | ||
msg.push(" --no-resolve-symlinks"); | ||
msg.push(" If passed, rootPaths that are symlinks will not be resolved (the default)."); | ||
msg.push(""); | ||
msg.push("Cluster Options:"); | ||
@@ -73,0 +78,0 @@ msg.push(" --cluster Enable clustering of server across multiple processes."); |
@@ -47,3 +47,3 @@ var fs = require('fs'), | ||
// error. | ||
rootPathResolved = resolvePathSync(config.rootPath); | ||
rootPathResolved = resolvePathSync(config.rootPath, config.resolveSymlinks); | ||
} | ||
@@ -50,0 +50,0 @@ |
@@ -46,3 +46,3 @@ /** | ||
// cache config object used in middleware | ||
dynamicPathMiddleware.CONFIG = parseConfig(options.rootPath); | ||
dynamicPathMiddleware.CONFIG = parseConfig(options.rootPath, options.resolveSymlinks); | ||
@@ -59,8 +59,8 @@ return dynamicPathMiddleware; | ||
dynamicParams.forEach(function (dynamicParam) { | ||
dynamicParams.forEach(function (dynamicParam, idx) { | ||
var dynamicValue = dynamicParam && params[dynamicParam]; | ||
if (dynamicValue) { | ||
// rootSuffixes contribute to cache key | ||
if (rootSuffixes[dynamicParam]) { | ||
dynamicValue = path.join(dynamicValue, rootSuffixes[dynamicParam]); | ||
if (rootSuffixes[idx]) { | ||
dynamicValue = path.join(dynamicValue, rootSuffixes[idx]); | ||
} | ||
@@ -107,3 +107,4 @@ | ||
@method parseConfig | ||
@param {String} rootPatha | ||
@param {String} rootPath | ||
@param {Boolean} resolveSymlinks | ||
@return {Object} config | ||
@@ -116,3 +117,3 @@ @property {String} config.rootPath | ||
**/ | ||
function parseConfig(rootPath) { | ||
function parseConfig(rootPath, resolveSymlinks) { | ||
rootPath = path.normalize(rootPath); | ||
@@ -122,3 +123,3 @@ | ||
var dynamicParams = []; | ||
var rootSuffixes = {}; | ||
var rootSuffixes = []; | ||
@@ -139,7 +140,10 @@ // str.match() in route config returns null if no matches or [":foo"] | ||
// rootSuffixes must be stored for use in getDynamicRoot | ||
rootSuffixes[keyName] = rootPath.substr(rootPath.indexOf(dynamicKey) + dynamicKey.length); | ||
rootSuffixes.unshift(rootPath.split(dynamicKey).pop()); | ||
} else { | ||
// maintain correct indices with non-matching keys | ||
rootSuffixes.unshift(''); | ||
} | ||
// remove key + suffix from rootPath used in initial realpathSync | ||
rootPath = rootPath.substring(0, rootPath.indexOf(dynamicKey)); | ||
// remove key + suffix from rootPath used in initial resolvePathSync | ||
rootPath = rootPath.substring(0, rootPath.lastIndexOf(dynamicKey)); | ||
}); | ||
@@ -150,3 +154,3 @@ } | ||
// middleware is initialized, and we want it to throw if there's an error. | ||
rootPath = resolvePathSync(rootPath); | ||
rootPath = resolvePathSync(rootPath, resolveSymlinks); | ||
@@ -153,0 +157,0 @@ return { |
@@ -46,7 +46,16 @@ /** | ||
function resolvePathSync(rootPath) { | ||
function resolvePathSync(rootPath, resolveSymlinks) { | ||
// Turns out fs.realpathSync always defaults empty strings to cwd(). | ||
rootPath = rootPath || process.cwd(); | ||
// Intentionally using the sync method because this only runs when the | ||
// middleware is initialized, and we want it to throw if there's an error. | ||
if (resolveSymlinks !== false) { | ||
rootPath = fs.realpathSync(rootPath); | ||
} else { | ||
fs.statSync(rootPath); | ||
} | ||
// The resulting rootPath should always have a trailing slash. | ||
return path.normalize(fs.realpathSync(rootPath || '') + path.sep); | ||
return path.normalize(rootPath + path.sep); | ||
} |
{ | ||
"name" : "combohandler", | ||
"description": "Simple Yahoo!-style combo handler.", | ||
"version" : "0.3.6", | ||
"version" : "0.3.7", | ||
"keywords" : [ | ||
@@ -47,7 +47,7 @@ "combo", "combohandler", "combohandle", "combine", "cdn", "css", "yui" | ||
"istanbul": "0.1.44", | ||
"jshint" : "2.1.11", | ||
"jshint" : "2.3.0", | ||
"mocha" : "1.13.0", | ||
"request" : "2.27.0", | ||
"rimraf" : "2.2.2", | ||
"should" : "2.0.1", | ||
"should" : "2.0.2", | ||
"sinon" : "1.7.3" | ||
@@ -54,0 +54,0 @@ }, |
@@ -590,5 +590,17 @@ /*global describe, before, after, it, sinon */ | ||
app.get("/:major/separated/by/:minor", | ||
app.get("/:major/separated/route/:minor", | ||
combo.combine({ rootPath: FIXTURES_DIR + "/:major/:minor/static" }), | ||
combo.respond); | ||
app.get("/:major/separated/path/:minor", | ||
combo.combine({ rootPath: FIXTURES_DIR + "/:major/decafbad/:minor" }), | ||
combo.respond); | ||
app.get('/doubled-in-rootpath/:major', | ||
combo.combine({ rootPath: FIXTURES_DIR + '/dynamic/:major/:major' }), | ||
combo.respond); | ||
app.get('/doubled-with-suffixes/:major', | ||
combo.combine({ rootPath: FIXTURES_DIR + '/dynamic/:major/static/:major' }), | ||
combo.respond); | ||
}); | ||
@@ -612,5 +624,20 @@ | ||
it("should resolve route that has separated parameters", assertResponds({ | ||
path: "/dynamic/separated/by/decafbad?c.js&d.js", | ||
path: "/dynamic/separated/route/decafbad?c.js&d.js", | ||
body: "c();\n\nd();\n" | ||
})); | ||
it("should resolve root path that has separated parameters", assertResponds({ | ||
path: "/dynamic/separated/path/static?c.js&d.js", | ||
body: "c();\n\nd();\n" | ||
})); | ||
it("should resolve route that has identical parameters in root path", assertResponds({ | ||
path: "/doubled-in-rootpath/baddecaf?e.js&f.js", | ||
body: "e();\n\nf();\n" | ||
})); | ||
it("should resolve route that has separated identical parameters in root path", assertResponds({ | ||
path: "/doubled-with-suffixes/cafebabe?g.js&h.js", | ||
body: "g();\n\nh();\n" | ||
})); | ||
}); | ||
@@ -652,2 +679,38 @@ }); | ||
function dynamicFiletree(opts) { | ||
var expectedRelativePath = opts.relativePath || "js/a.js"; | ||
var expectedResolvedPath = path.join(COMPLEX_ROOT, opts.realPath, expectedRelativePath); | ||
var expectedRootPath = path.join(COMPLEX_ROOT, opts.rootPath); | ||
return function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(expectedRootPath); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal(expectedRelativePath); | ||
fs.realpath(path.join(rootPath, relativePath), function (err, resolved) { | ||
assert.ifError(err); | ||
resolved.should.equal(expectedResolvedPath); | ||
next(); | ||
}); | ||
}; | ||
} | ||
function dynamicSymlinks(opts) { | ||
var expectedTemplateFile = opts.template || TEMPLATE_SIMPLE; | ||
var expectedRelativePath = opts.relativePath || "css/urls/simple.css"; | ||
var expectedResolvedBody = expectedTemplateFile.replace(/__ROOT__/g, opts.rootPath); | ||
return function (req, res, next) { | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal(expectedRelativePath); | ||
// console.error(res.body); | ||
res.body.should.equal(expectedResolvedBody); | ||
next(); | ||
}; | ||
} | ||
describe("route with fully-qualified dynamic path", function () { | ||
@@ -660,36 +723,17 @@ before(function () { | ||
app.get("/c/:version/fs-fq", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/versioned/deeper/base/')); | ||
next(); | ||
}, combo.respond); | ||
app.get("/c/:version/fs-fq", combined, dynamicFiletree({ | ||
realPath: "/versioned/deeper/base/", | ||
rootPath: "/versioned/deeper/base/" | ||
}), combo.respond); | ||
app.get("/c/:version/ln-fq", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/versioned/shallower/base/')); | ||
app.get("/c/:version/ln-fq", combined, dynamicFiletree({ | ||
realPath: "/base/", | ||
rootPath: "/versioned/shallower/base/" | ||
}), combo.respond); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('js/a.js'); | ||
fs.realpath(path.join(rootPath, relativePath), function (err, resolved) { | ||
assert.ifError(err); | ||
resolved.should.equal(path.join(COMPLEX_ROOT, '/base/', relativePath)); | ||
next(); | ||
}); | ||
}, combo.respond); | ||
app.get("/c/:version/fq-noimports", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/versioned/shallower/base/')); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('css/urls/simple.css'); | ||
// console.error(res.body); | ||
var expected = (SIMPLE_IMPORTS_RAW + TEMPLATE_URLS_SIMPLE) | ||
.replace(/__ROOT__/g, '/versioned/shallower/base/'); | ||
res.body.should.equal(expected); | ||
next(); | ||
}, combo.respond); | ||
app.get("/c/:version/fq-noimports", combined, dynamicSymlinks({ | ||
template: SIMPLE_IMPORTS_RAW + TEMPLATE_URLS_SIMPLE, | ||
realPath: "/versioned/shallower/base/", | ||
rootPath: "/versioned/shallower/base/" | ||
}), combo.respond); | ||
}); | ||
@@ -712,87 +756,113 @@ | ||
describe("and rootPath symlinked shallower", function () { | ||
before(function () { | ||
var combined = combo.combine({ | ||
rewriteImports: true, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/versioned/shallower/base/' | ||
describe("when resolveSymlinks is true", function () { | ||
before(function () { | ||
var resolved = combo.combine({ | ||
rewriteImports: true, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/versioned/shallower/base/' | ||
}); | ||
app.get("/r/:version/fs-shallow", resolved, dynamicFiletree({ | ||
realPath: "/base/", | ||
rootPath: "/base/" | ||
}), combo.respond); | ||
app.get("/r/:version/ln-shallow", resolved, dynamicSymlinks({ | ||
rootPath: "/base/" | ||
}), combo.respond); | ||
}); | ||
app.get("/c/:version/fs-shallow", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/base/')); | ||
it("should resolve files from realpath in filesystem", assertResponds({ | ||
path: "/r/cafebabe/fs-shallow?js/a.js&js/b.js" | ||
})); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('js/a.js'); | ||
it("should rewrite url() through symlink", assertResponds({ | ||
path: "/r/cafebabe/ln-shallow?css/urls/simple.css" | ||
})); | ||
}); | ||
fs.realpath(path.join(rootPath, relativePath), function (err, resolved) { | ||
assert.ifError(err); | ||
resolved.should.equal(path.join(COMPLEX_ROOT, '/base/', relativePath)); | ||
next(); | ||
describe("when resolveSymlinks is false", function () { | ||
before(function () { | ||
var symlinkd = combo.combine({ | ||
rewriteImports: true, | ||
resolveSymlinks: false, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/versioned/shallower/base/' | ||
}); | ||
}, combo.respond); | ||
app.get("/c/:version/ln-shallow", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/base/')); | ||
app.get("/s/:version/fs-shallow", symlinkd, dynamicFiletree({ | ||
realPath: "/base/", | ||
rootPath: "/versioned/shallower/base/" | ||
}), combo.respond); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('css/urls/simple.css'); | ||
app.get("/s/:version/ln-shallow", symlinkd, dynamicSymlinks({ | ||
rootPath: "/versioned/shallower/base/" | ||
}), combo.respond); | ||
}); | ||
// console.error(res.body); | ||
res.body.should.equal(TEMPLATE_SIMPLE.replace(/__ROOT__/g, '/base/')); | ||
it("should resolve files from symlink in filesystem", assertResponds({ | ||
path: "/s/cafebabe/fs-shallow?js/a.js&js/b.js" | ||
})); | ||
next(); | ||
}, combo.respond); | ||
it("should rewrite url() using symlink", assertResponds({ | ||
path: "/s/cafebabe/ln-shallow?css/urls/simple.css" | ||
})); | ||
}); | ||
}); | ||
it("should resolve files from realpath in filesystem", assertResponds({ | ||
path: "/c/cafebabe/fs-shallow?js/a.js&js/b.js" | ||
})); | ||
describe("and rootPath symlinked deeper", function () { | ||
describe("when resolveSymlinks is true", function () { | ||
before(function () { | ||
var resolved = combo.combine({ | ||
rewriteImports: true, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/deep-link/' | ||
}); | ||
it("should rewrite url() through symlink", assertResponds({ | ||
path: "/c/cafebabe/ln-shallow?css/urls/simple.css" | ||
})); | ||
}); | ||
app.get("/r/:version/fs-deeper", resolved, dynamicFiletree({ | ||
realPath: "/versioned/deeper/base/", | ||
rootPath: "/versioned/deeper/base/" | ||
}), combo.respond); | ||
describe("and rootPath symlinked deeper", function () { | ||
before(function () { | ||
var combined = combo.combine({ | ||
rewriteImports: true, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/deep-link/' | ||
app.get("/r/:version/ln-deeper", resolved, dynamicSymlinks({ | ||
rootPath: "/versioned/deeper/base/" | ||
}), combo.respond); | ||
}); | ||
app.get("/c/:version/fs-deeper", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/versioned/deeper/base/')); | ||
it("should read rootPath from filesystem directly", assertResponds({ | ||
path: "/r/cafebabe/fs-deeper?js/a.js&js/b.js" | ||
})); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('js/a.js'); | ||
it("should *still* rewrite url() through symlink", assertResponds({ | ||
path: "/r/cafebabe/ln-deeper?css/urls/simple.css" | ||
})); | ||
}); | ||
next(); | ||
}, combo.respond); | ||
describe("when resolveSymlinks is false", function () { | ||
before(function () { | ||
var symlinkd = combo.combine({ | ||
rewriteImports: true, | ||
resolveSymlinks: false, | ||
webRoot : COMPLEX_ROOT, | ||
rootPath: COMPLEX_ROOT + '/deep-link/' | ||
}); | ||
app.get("/c/:version/ln-deeper", combined, function (req, res, next) { | ||
var rootPath = res.locals.rootPath; | ||
rootPath.should.equal(path.join(COMPLEX_ROOT, '/versioned/deeper/base/')); | ||
app.get("/s/:version/fs-deeper", symlinkd, dynamicFiletree({ | ||
realPath: "/versioned/deeper/base/", | ||
rootPath: "/deep-link/" | ||
}), combo.respond); | ||
var relativePath = res.locals.relativePaths[0]; | ||
relativePath.should.equal('css/urls/simple.css'); | ||
app.get("/s/:version/ln-deeper", symlinkd, dynamicSymlinks({ | ||
rootPath: "/deep-link/" | ||
}), combo.respond); | ||
}); | ||
// console.error(res.body); | ||
var expected = TEMPLATE_SIMPLE | ||
.replace(/__ROOT__/g, '/versioned/deeper/base/'); | ||
res.body.should.equal(expected); | ||
it("should read rootPath from symlink in filesystem", assertResponds({ | ||
path: "/s/cafebabe/fs-deeper?js/a.js&js/b.js" | ||
})); | ||
next(); | ||
}, combo.respond); | ||
it("should *still* rewrite url() using symlink", assertResponds({ | ||
path: "/s/cafebabe/ln-deeper?css/urls/simple.css" | ||
})); | ||
}); | ||
it("should read rootPath from filesystem directly", assertResponds({ | ||
path: "/c/cafebabe/fs-deeper?js/a.js&js/b.js" | ||
})); | ||
it("should *still* rewrite url() through symlink", assertResponds({ | ||
path: "/c/cafebabe/ln-deeper?css/urls/simple.css" | ||
})); | ||
}); | ||
@@ -799,0 +869,0 @@ }); |
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
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
167256
57
3356