Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

combohandler

Package Overview
Dependencies
Maintainers
2
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

combohandler - npm Package Compare versions

Comparing version 0.2.1 to 0.2.2

.tm_properties

10

config.sample.js

@@ -10,3 +10,13 @@ module.exports = {

//
// The :version placeholder is used to version the combo-handled requests by
// "global" directory name (usually a sha1 hash), instead of per-file.
//
// The URL and local path must both contain the placeholder ":version"
//
// http://example.com/84b94bb/combo?build/mod-a/mod-a-min.js&build/mod-b/mod-b-min.js
// resolves to:
// /local/path/to/84b94bb/build/mod-a/mod-a-min.js
// /local/path/to/84b94bb/build/mod-b/mod-b-min.js
roots: {
'/:version/combo': '/local/path/to/:version',
'/yui3': '/local/path/to/yui3'

@@ -13,0 +23,0 @@ },

Combo Handler History
=====================
0.2.2 (2013-04-21)
------------------
* Changed default `combine` middleware to return an array of middleware instead
of a function, allowing addition of bundled middlware depending on configured
values. (Express flattens any arrays passed as route callbacks)
* Changed default error handler to pass the error to `next()` when it isn't a
`BadRequest`, which was itself extracted to a separate file.
* Changed CSS `url()` rewriter to use built-in `path` methods instead of custom
algorithm, with expanded test coverage.
* Added `bodyContents` and `relativePaths` arrays to `res.locals` object in the
default `combine` middleware, allowing subsequent middleware access to those
values.
* Added `cssUrls` middleware, an extraction of the CSS `url()` rewriter, with
the optional capability of rewriting `@import` statements as well.
* Added `dynamicPath` middleware, supporting route params (e.g., `:version`)
that point to different trees under the same filesystem root.
* Added `respond` middleware for convenience. It simply responds 200 with the
contents of the `res.body` property.
* Added code coverage with `istanbul`.
* Added Travis support.
* Added [Daniel Stockman](https://github.com/evocateur) as a maintainer.
* Updated mocha dependency to 1.9.0.
* Updated express dependency to 3.2.x. [Eric Ferraiuolo]
0.2.1 (2013-04-01)

@@ -5,0 +41,0 @@ ------------------

106

lib/combohandler.js
var fs = require('fs'),
path = require('path'),
util = require('util'),
URI = require("URIjs"),
// exported to allow instanceof checks
BadRequest = exports.BadRequest = require('./error/bad-request'),
// Default set of MIME types supported by the combo handler. Attempts to

@@ -19,10 +20,13 @@ // combine one or more files with an extension not in this mapping (or not

exports.combine = function (config) {
var maxAge = config.maxAge,
config = config || {};
var callbacks = [],
maxAge = config.maxAge,
mimeTypes = config.mimeTypes || MIME_TYPES,
basePath = config.basePath || "",
rootPathResolved;
// 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.
rootPath = fs.realpathSync(config.rootPath);
// Express flattens any arrays passed as route callbacks.
// By always returning an array, we can add middleware based on config.
callbacks.push(combineMiddleware);

@@ -32,8 +36,17 @@ if (typeof maxAge === 'undefined') {

}
if ((/[^\/]$/).test(basePath)) {
basePath += "/";
if (basePath) {
callbacks.push(exports.middleware.cssUrls(config));
}
return function (req, res, next) {
if (config.rootPath && /:\w+/.test(config.rootPath)) {
callbacks.unshift(exports.middleware.dynamicPath(config));
} else {
// 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.
rootPathResolved = fs.realpathSync(config.rootPath || '');
}
function combineMiddleware(req, res, next) {
var body = [],

@@ -45,3 +58,3 @@ query = parseQuery(req.url),

type = fileTypes.length === 1 && mimeTypes[fileTypes[0]],
rewrite = basePath && ("text/css" === type),
rootPath = res.locals.rootPath || rootPathResolved,
lastModified;

@@ -60,3 +73,13 @@

res.header('Content-Type', (type || 'text/plain') + ';charset=utf-8');
// charset must be specified before contentType
// https://github.com/visionmedia/express/issues/1261
res.charset = 'utf-8';
res.contentType(type);
// provide metadata to subsequent middleware via res.locals
res.locals({
bodyContents: body,
relativePaths: query
});
res.body = body.join('\n');

@@ -117,3 +140,3 @@

body[i] = rewrite ? rewriteCSSURLs(basePath, relativePath, data) : data;
body[i] = data;
pending -= 1;

@@ -127,16 +150,11 @@

}); // forEach
};
}
return callbacks;
};
// BadRequest is used for all filesystem-related errors, including when a
// requested file can't be found (a NotFound error wouldn't be appropriate in
// that case since the route itself exists; it's the request that's at fault).
function BadRequest(message) {
Error.call(this);
this.name = 'BadRequest';
this.message = message;
Error.captureStackTrace(this, arguments.callee);
}
util.inherits(BadRequest, Error);
exports.BadRequest = BadRequest; // exported to allow instanceof checks
// By convention, this is the last middleware passed to any combo route
exports.respond = function respondMiddleware(req, res) {
res.send(res.body);
};

@@ -196,27 +214,11 @@ // -- Private Methods ----------------------------------------------------------

function rewriteCSSURLs(base, path, data) {
return data.replace(/[\s:]url\(\s*(['"]?)(\S+)\1\s*\)/g, function (substr, quote, match) {
var root,
dest;
// There is a ton of complexity related to URL parsing related
// to unicode, escapement, etc. Rather than try to capture that,
// this just does a simple sniff to validate whether the URL
// needs to be rewritten.
if (!URI(match).is("relative")) {
return substr;
}
// the current directory
root = (base + path).split("/"),
// the place to which we're traversing
dest = match.split("/");
// pull off the file name
root.pop();
// while the destination contains "../", knock that off and
// knock off the last directory of root
while (dest.length > 1 && ".." === dest[0]) {
root.pop();
dest.shift();
}
return substr.replace(match, root.concat(dest).join("/"));
});
}
// Auto-load bundled middleware with getters, Connect-style
exports.middleware = {};
fs.readdirSync(__dirname + '/middleware').forEach(function (filename) {
if (!/\.js$/.test(filename)) { return; }
var name = path.basename(filename, '.js');
function load() { return require('./middleware/' + name); }
exports.middleware.__defineGetter__(name, load);
exports.__defineGetter__(name, load);
});

@@ -37,3 +37,3 @@ var combo = require('./combohandler'),

} else {
next();
next(err);
}

@@ -44,5 +44,3 @@ });

for (route in roots) {
app.get(route, combo.combine({rootPath: roots[route]}), function (req, res) {
res.send(res.body);
});
app.get(route, combo.combine({rootPath: roots[route]}), combo.respond);
}

@@ -49,0 +47,0 @@

{
"name" : "combohandler",
"description": "Simple Yahoo!-style combo handler.",
"version" : "0.2.1",
"version" : "0.2.2",
"keywords" : [

@@ -27,8 +27,9 @@ "combo", "combohandler", "combohandle", "combine", "cdn", "css", "yui"

"dependencies": {
"express": "3.1.1",
"URIjs" : "1.10.0"
"express": "3.2.x",
"URIjs" : "1.10.1"
},
"devDependencies": {
"mocha" : "1.6.0",
"istanbul": "~0.1.34",
"mocha" : "1.9.0",
"request": "~2.9",

@@ -43,4 +44,4 @@ "should" : "1.2.0"

"scripts": {
"test": "./node_modules/.bin/mocha"
"test": "istanbul test --print both ./node_modules/mocha/bin/_mocha"
}
}
Combo Handler
=============
[![Build Status](https://travis-ci.org/rgrove/combohandler.png?branch=master)](https://travis-ci.org/rgrove/combohandler)
This is a simple combo handler for Node.js, usable either as [Connect][]

@@ -59,5 +61,3 @@ middleware or as an [Express][] server. It works just like the combo handler

```js
app.get('/foo', combo.combine({rootPath: '/local/path/to/foo'}), function (req, res) {
res.send(res.body);
});
app.get('/foo', combo.combine({rootPath: '/local/path/to/foo'}), combo.respond);
```

@@ -98,5 +98,5 @@

res.type('text/plain');
res.send(400, 'Bad request.');
res.send(400, 'Bad request. ' + err.message);
} else {
next();
next(err);
}

@@ -110,5 +110,3 @@ });

//
app.get('/yui3', combo.combine({rootPath: '/local/path/to/yui3'}), function (req, res) {
res.send(res.body);
});
app.get('/yui3', combo.combine({rootPath: '/local/path/to/yui3'}), combo.respond);

@@ -118,2 +116,14 @@ app.listen(3000);

#### `combo.respond`
The `respond` method exported by `require('combohandler')` is a convenience method intended to be the last callback passed to an [express route](http://expressjs.com/api.html#app.VERB). Unless you have a *very* good reason to avoid it, you should probably use it. Here is the equivalent callback:
```js
function respond(req, res) {
res.send(res.body);
}
```
This method may be extended in the future to do fancy things with optional combohandler middleware.
### Creating a server

@@ -189,10 +199,65 @@

//
app.get('/combo', combohandler.combine({
app.get('/combo', combo.combine({
rootPath: __dirname + '/public',
basePath: '/public'
}), function (req, res) {
res.send(res.body);
});
}), combo.respond);
```
Alternatively, you can use the built-in `cssUrls` middleware as a separate
route callback. `cssUrls` must always be placed after the default `combine`
middleware when used in this fashion.
```js
// This route provides the same behaviour as the previous example, providing
// better separation of concerns and the possibility of inserting custom
// middleware between the built-in steps.
app.get('/combo',
combo.combine({
rootPath: __dirname + '/public'
}),
combo.cssUrls({
basePath: '/public'
}),
combo.respond);
```
Finally, the `cssUrls` middleware has the ability (disabled by default) to
rewrite `@import` paths in the same manner as `url()` values. As `@import` is
considered an anti-pattern in production code, this functionality is strictly
opt-in and requires passing `true` as the `rewriteImports` property in the
middleware options object.
```js
// Automagically
app.get('/combo', combo.combine({
rootPath: __dirname + '/public',
basePath: '/public',
rewriteImports: true
}), combo.respond);
// As explicit middleware
app.get('/combo',
combo.combine({ rootPath: __dirname + '/public' }),
combo.cssUrls({ basePath: '/public', rewriteImports: true }),
combo.respond);
```
### Dynamic Paths via Route Parameters
To enable resolution of dynamic subtree paths under a given `rootPath`, simply add a [route parameter](http://expressjs.com/api.html#req.params) to both the route and the `rootPath` config.
```js
app.get('/combo/yui/:version', combo.combine({
rootPath: '/local/path/to/yui/:version/build'
}), combo.respond);
```
Given this config, any [YUI release tarball](http://yuilibrary.com/download/yui3/) you explode into a versioned subdirectory of `/local/path/to/yui/` would be available under a much shorter URL than the default config provides:
http://example.com/combo/yui/3.9.1?yui/yui-min.js&yui-throttle/yui-throttle-min.js
// vs
http://example.com/combo/yui?3.9.1/build/yui/yui-min.js&3.9.1/build/yui-throttle/yui-throttle-min.js
If the built-in `dynamicPath` middleware is used manually, it _must_ be inserted *before* the default `combine` middleware.
Using as a YUI 3 combo handler

@@ -199,0 +264,0 @@ ------------------------------

@@ -0,1 +1,2 @@

/*global describe, before, after, it */
var combo = require('../'),

@@ -5,3 +6,2 @@ server = require('../lib/server'),

assert = require('assert'),
express = require('express'),
request = require('request'),

@@ -21,18 +21,5 @@

'/css': __dirname + '/fixtures/root/css',
'/js' : __dirname + '/fixtures/root/js',
'/norewrite': __dirname + '/fixtures/rewrite'
'/js' : __dirname + '/fixtures/root/js'
}
});
app.get('/rewrite', combo.combine({
rootPath: __dirname + '/fixtures/rewrite',
basePath: "/rewritten"
}), function (req, res) {
res.send(res.body);
});
app.get('/rewrite-noslash', combo.combine({
rootPath: __dirname + '/fixtures/rewrite',
basePath: "/rewritten/"
}), function (req, res) {
res.send(res.body);
});

@@ -48,5 +35,5 @@ httpServer = app.listen(PORT);

request(BASE_URL + '/js?a.js&b.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript;charset=utf-8');
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');

@@ -60,5 +47,5 @@ body.should.equal('a();\n\nb();\n');

request(BASE_URL + '/css?a.css&b.css', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'text/css;charset=utf-8');
res.should.have.header('content-type', 'text/css; charset=utf-8');
res.should.have.header('last-modified');

@@ -72,5 +59,5 @@ body.should.equal('.a { color: green; }\n\n.b { color: green; }\n');

request(BASE_URL + '/js?a.js&b.js&outside.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript;charset=utf-8');
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');

@@ -88,5 +75,3 @@ body.should.equal('a();\n\nb();\n\noutside();\n');

maxAge : null
}), function (req, res) {
res.send(res.body, 200);
});
}), combo.respond);

@@ -96,5 +81,3 @@ app.get('/max-age-0', combo.combine({

maxAge : 0
}), function (req, res) {
res.send(res.body, 200);
});
}), combo.respond);
});

@@ -104,3 +87,3 @@

request(BASE_URL + '/js?a.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);

@@ -118,3 +101,3 @@ res.should.have.header('cache-control', 'public,max-age=31536000');

request(BASE_URL + '/max-age-null?a.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);

@@ -129,3 +112,3 @@ res.headers.should.not.have.property('cache-control');

request(BASE_URL + '/max-age-0?a.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(200);

@@ -144,5 +127,44 @@ res.should.have.header('cache-control', 'public,max-age=0');

describe('errors', function () {
before(function () {
app.get('/error-next?', function (req, res, next) {
var poo = new Error('poo');
poo.stack = null; // silence irrelevant output
next(poo);
}, combo.combine({
rootPath: __dirname + '/fixtures/root/js'
}), combo.respond);
app.get('/error-throw?', combo.combine({
rootPath: __dirname + '/fixtures/root/js'
}), function (req, res, next) {
throw 'poo';
}, combo.respond);
});
it('should inherit from Error', function () {
var err = new combo.BadRequest('test');
err.should.be.an.instanceOf(Error);
err.name.should.equal('BadRequest');
err.message.should.equal('test');
});
it('should return a 500 when error before middleware', function (done) {
request(BASE_URL + '/error-next?a.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(500);
done();
});
});
it('should return a 500 when error after middleware', function (done) {
request(BASE_URL + '/error-throw?a.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(500);
done();
});
});
it('should return a 400 Bad Request error when no files are specified', function (done) {
request(BASE_URL + '/js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -156,3 +178,3 @@ body.should.equal('Bad request. No files requested.');

request(BASE_URL + '/js?bogus.js', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -167,3 +189,3 @@ res.should.have.header('content-type', 'text/plain; charset=utf-8');

request(BASE_URL + '/js?foo.bar', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -177,3 +199,3 @@ body.should.equal('Bad request. Illegal MIME type present.');

request(BASE_URL + '/js?a.js&foo.bar', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -187,3 +209,3 @@ body.should.equal('Bad request. Only one MIME type allowed per request.');

request(BASE_URL + '/js?a.js&b.css', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -197,3 +219,3 @@ body.should.equal('Bad request. Only one MIME type allowed per request.');

request(BASE_URL + '/js?a.js&b', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -207,3 +229,3 @@ body.should.equal('Bad request. Truncated query parameters.');

request(BASE_URL + '/js?a', function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -234,3 +256,3 @@ body.should.equal('Bad request. Truncated query parameters.');

request(BASE_URL + '/js?' + path, function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
res.should.have.status(400);

@@ -248,5 +270,27 @@ res.should.have.header('content-type', 'text/plain; charset=utf-8');

describe("url rewrites", function () {
it ("should allow the basePath to end in a slash", function (done) {
before(function () {
app.get('/norewrite', combo.combine({
rootPath: __dirname + '/fixtures/rewrite'
}), combo.respond);
app.get('/rewrite', combo.combine({
rootPath: __dirname + '/fixtures/rewrite',
basePath: "/rewritten"
}), combo.respond);
app.get('/rewrite-noslash', combo.combine({
rootPath: __dirname + '/fixtures/rewrite',
basePath: "/rewritten/"
}), combo.respond);
app.get('/rewrite-imports', combo.combine({
rootPath: __dirname + '/fixtures/rewrite',
basePath: "/rewritten/",
rewriteImports: true
}), combo.respond);
});
it("should allow the basePath to end in a slash", function (done) {
request(BASE_URL + "/rewrite-noslash?urls.css", function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
body.should.equal([

@@ -262,2 +306,8 @@ "#no-quotes { background: url(/rewritten/no-quotes.png);}",

"#escaped-stuff { background:url(\"/rewritten/\\)\\\";\\'\\(.png\"); }",
".unicode-raw { background: url(/rewritten/déchaîné.png); }",
".unicode-escaped { background: url(/rewritten/d\\0000E9cha\\EEn\\E9.png); }",
".nl-craziness { background:",
" url(/rewritten/crazy.png",
" ); }",
""
].join("\n"));

@@ -267,5 +317,6 @@ done();

});
it ("should not rewrite without a basePath", function (done) {
it("should not rewrite without a basePath", function (done) {
request(BASE_URL + "/norewrite?urls.css", function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
body.should.equal([

@@ -280,3 +331,9 @@ "#no-quotes { background: url(no-quotes.png);}",

"#protocol-relative-url { background: url(//www.example.com/foo.gif?a=b&c=d#bebimbop);}",
"#escaped-stuff { background:url(\"\\)\\\";\\'\\(.png\"); }"
"#escaped-stuff { background:url(\"\\)\\\";\\'\\(.png\"); }",
".unicode-raw { background: url(déchaîné.png); }",
".unicode-escaped { background: url(d\\0000E9cha\\EEn\\E9.png); }",
".nl-craziness { background:",
" url(crazy.png",
" ); }",
""
].join("\n"));

@@ -286,5 +343,6 @@ done();

});
it ("should rewrite valid urls", function (done) {
it("should rewrite valid urls", function (done) {
request(BASE_URL + "/rewrite?urls.css&deeper/more.css", function (err, res, body) {
assert.equal(err, null);
assert.ifError(err);
body.should.equal([

@@ -300,2 +358,10 @@ "#no-quotes { background: url(/rewritten/no-quotes.png);}",

"#escaped-stuff { background:url(\"/rewritten/\\)\\\";\\'\\(.png\"); }",
".unicode-raw { background: url(/rewritten/déchaîné.png); }",
// NOTE: we do not currently support the space terminator for CSS escapes.
// ".unicode-escaped { background: url(/rewritten/d\\E9 cha\\EEn\\E9.png); }",
".unicode-escaped { background: url(/rewritten/d\\0000E9cha\\EEn\\E9.png); }",
".nl-craziness { background:",
" url(/rewritten/crazy.png",
" ); }",
"",
"#depth { background: url(/rewritten/deeper/deeper.png);}",

@@ -308,3 +374,231 @@ "#up-one { background: url(/rewritten/shallower.png);}",

});
it("should rewrite import paths when enabled from combine", function (done) {
request(BASE_URL + "/rewrite-imports?imports.css", function (err, res, body) {
assert.ifError(err);
body.should.equal([
"@import '/rewritten/basic-sq.css';",
"@import \"/rewritten/basic-dq.css\";",
"@import url(/rewritten/url-uq.css);",
"@import url('/rewritten/url-sq.css');",
"@import url(\"/rewritten/url-dq.css\");",
"@import \"/rewritten/media-simple.css\" print;",
"@import url(\"/rewritten/media-simple-url.css\") print;",
"@import '/rewritten/media-simple-comma.css' print, screen;",
"@import \"/rewritten/media-complex.css\" screen and (min-width: 400px) and (max-width: 700px);",
"@import url(\"/rewritten/media-complex-url.css\") screen and (min-width: 400px) and (max-width: 700px);",
// TODO: are the following rewritten correctly?
"@import \"/rewrite/deeper/more.css\";",
"@import \"/root/css/a.css\" (device-width: 320px);",
""
].join("\n"));
done();
});
});
describe("as middleware", function () {
before(function () {
app.get('/rewrite-middleware-ignore',
combo.combine({ rootPath: __dirname + '/fixtures/root/js' }),
combo.cssUrls({ basePath: "/rewritten/" }),
combo.respond);
app.get('/rewrite-middleware',
combo.combine({ rootPath: __dirname + '/fixtures/rewrite' }),
combo.cssUrls({ basePath: "/rewritten/" }),
combo.respond);
app.get('/rewrite-middleware-imports',
combo.combine({ rootPath: __dirname + '/fixtures/rewrite' }),
combo.cssUrls({ basePath: "/rewritten/", rewriteImports: true }),
combo.respond);
});
it("should avoid modifying non-CSS requests", function (done) {
request(BASE_URL + '/rewrite-middleware-ignore?a.js&b.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('a();\n\nb();\n');
done();
});
});
it("should rewrite valid urls", function (done) {
request(BASE_URL + "/rewrite-middleware?urls.css&deeper/more.css", function (err, res, body) {
assert.ifError(err);
body.should.equal([
"#no-quotes { background: url(/rewritten/no-quotes.png);}",
"#single-quotes { background: url(\'/rewritten/single-quotes.png\');}",
"#double-quotes { background: url(\"/rewritten/double-quotes.png\");}",
"#spaces { background: url(",
" \"/rewritten/spaces.png\" );}",
"#data-url { background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==);}",
"#absolute-url { background: url(http://www.example.com/foo.gif?a=b&c=d#bebimbop);}",
"#protocol-relative-url { background: url(//www.example.com/foo.gif?a=b&c=d#bebimbop);}",
"#escaped-stuff { background:url(\"/rewritten/\\)\\\";\\'\\(.png\"); }",
".unicode-raw { background: url(/rewritten/déchaîné.png); }",
".unicode-escaped { background: url(/rewritten/d\\0000E9cha\\EEn\\E9.png); }",
".nl-craziness { background:",
" url(/rewritten/crazy.png",
" ); }",
"",
"#depth { background: url(/rewritten/deeper/deeper.png);}",
"#up-one { background: url(/rewritten/shallower.png);}",
"#down-one { background: url(/rewritten/deeper/more/down-one.png);}"
].join("\n"));
done();
});
});
it("should NOT rewrite import paths when disabled", function (done) {
request(BASE_URL + "/rewrite-middleware?imports.css", function (err, res, body) {
assert.ifError(err);
body.should.equal([
"@import 'basic-sq.css';",
"@import \"basic-dq.css\";",
"@import url(url-uq.css);",
"@import url('url-sq.css');",
"@import url(\"url-dq.css\");",
"@import \"media-simple.css\" print;",
"@import url(\"media-simple-url.css\") print;",
"@import 'media-simple-comma.css' print, screen;",
"@import \"media-complex.css\" screen and (min-width: 400px) and (max-width: 700px);",
"@import url(\"media-complex-url.css\") screen and (min-width: 400px) and (max-width: 700px);",
"@import \"../rewrite/deeper/more.css\";",
"@import \"../root/css/a.css\" (device-width: 320px);",
""
].join("\n"));
done();
});
});
it("should rewrite import paths when enabled", function (done) {
request(BASE_URL + "/rewrite-middleware-imports?imports.css", function (err, res, body) {
assert.ifError(err);
body.should.equal([
"@import '/rewritten/basic-sq.css';",
"@import \"/rewritten/basic-dq.css\";",
"@import url(/rewritten/url-uq.css);",
"@import url('/rewritten/url-sq.css');",
"@import url(\"/rewritten/url-dq.css\");",
"@import \"/rewritten/media-simple.css\" print;",
"@import url(\"/rewritten/media-simple-url.css\") print;",
"@import '/rewritten/media-simple-comma.css' print, screen;",
"@import \"/rewritten/media-complex.css\" screen and (min-width: 400px) and (max-width: 700px);",
"@import url(\"/rewritten/media-complex-url.css\") screen and (min-width: 400px) and (max-width: 700px);",
// TODO: are the following rewritten correctly?
"@import \"/rewrite/deeper/more.css\";",
"@import \"/root/css/a.css\" (device-width: 320px);",
""
].join("\n"));
done();
});
});
});
});
// Dynamic Paths ----------------------------------------------------------
describe("dynamic paths", function () {
before(function () {
app.get('/dynamic/:version',
combo.combine({ rootPath: __dirname + '/fixtures/dynamic/:version' }),
combo.respond);
app.get('/:version/param-first',
combo.combine({ rootPath: __dirname + '/fixtures/dynamic/:version' }),
combo.respond);
app.get('/dynamic/:version/empty-combo',
combo.dynamicPath({ rootPath: __dirname + '/fixtures/dynamic/:version/static' }),
combo.combine(),
combo.respond);
app.get('/dynamic/:version/doubled',
combo.dynamicPath({ rootPath: __dirname + '/fixtures/dynamic/:version/static' }),
combo.combine({ rootPath: __dirname + '/fixtures/dynamic/:version/static' }),
combo.respond);
app.get('/non-dynamic',
combo.dynamicPath({ rootPath: __dirname + '/fixtures/dynamic/decafbad' }),
combo.combine({ rootPath: __dirname + '/fixtures/dynamic/decafbad' }),
combo.respond);
});
it("should work when param found at end of path", function (done) {
request(BASE_URL + '/dynamic/decafbad?a.js&b.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('a();\n\nb();\n');
done();
});
});
it("should work when param found at beginning of path", function (done) {
request(BASE_URL + '/decafbad/param-first?a.js&static/c.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('a();\n\nc();\n');
done();
});
});
it("should work when rootPath not passed to combine()", function (done) {
request(BASE_URL + '/dynamic/decafbad/empty-combo?c.js&d.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('c();\n\nd();\n');
done();
});
});
it("should work when param found before end of path", function (done) {
request(BASE_URL + '/dynamic/decafbad/empty-combo?c.js&d.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('c();\n\nd();\n');
done();
});
});
it("should work when middleware is run twice on same route", function (done) {
request(BASE_URL + '/dynamic/decafbad/doubled?c.js&d.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('c();\n\nd();\n');
done();
});
});
it("should not fail when param not present", function (done) {
request(BASE_URL + '/non-dynamic?a.js&b.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(200);
res.should.have.header('content-type', 'application/javascript; charset=utf-8');
res.should.have.header('last-modified');
body.should.equal('a();\n\nb();\n');
done();
});
});
it("should error when param does not correspond to existing path", function (done) {
request(BASE_URL + '/dynamic/deadbeef?a.js', function (err, res, body) {
assert.ifError(err);
res.should.have.status(400);
body.should.equal('Bad request. Unable to resolve path: /dynamic/deadbeef');
done();
});
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc