Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions bin/livestyle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var express = require('express'),
util = require('util'),
path = require('path'),
mappings = {},
replacements = {},
optimist = require('optimist'),
commandLineOptions = optimist
.usage('Usage: $0 [--proxy <remotehost> [-m <directoryMapping>]] [-d] [-h <hostname>] [-p <port>] [--root directory]')
Expand Down Expand Up @@ -58,6 +59,9 @@ var express = require('express'),
alias: 'm',
describe: 'Directory mappings of the form: remoteDir=localDir where localDir is relative to the root directory unless prefixed with /'
})
.options('replace', {
describe: 'Low level string replacement run on your html response strings. Useful to reroute http URLs to local disk. This could break your html!'
})
.options('host', {
alias: 'h',
default: '0.0.0.0',
Expand Down Expand Up @@ -85,6 +89,17 @@ var express = require('express'),
}
});
}

if (argv.replace) {
(typeof argv.replace === 'string' ? [argv.replace] : argv.replace).forEach(function (urlEqualsDir) {
var matchUrlEqualsDir = urlEqualsDir.match(/^([^=]+)=([^=]+)$/);
if (matchUrlEqualsDir) {
replacements[matchUrlEqualsDir[1]] = matchUrlEqualsDir[2];
} else {
throw 'Invalid --replace syntax: ' + urlEqualsDir;
}
});
}
})
.argv,
root;
Expand Down Expand Up @@ -123,6 +138,7 @@ var server = require('../lib/createLiveStyleApp')({
jsxtransform: commandLineOptions.jsxtransform,
processImage: commandLineOptions.processimage,
mappings: mappings,
replacements: replacements,
proxy: commandLineOptions.proxy || null
}).listen(commandLineOptions.port, commandLineOptions.host);

Expand Down
4 changes: 4 additions & 0 deletions lib/createLiveStyleApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ module.exports = function createLiveStyleApp(options) {

app.use(require('./middleware/bufferDataEventsUntilFirstListener')());

if (options.replacements) {
app.use(require('./middleware/replaceString')(options.replacements));
}

if (!options.dead) {
app.use(require('./middleware/injectLiveStyleScriptIncludeIntoHtml')(options));
}
Expand Down
56 changes: 56 additions & 0 deletions lib/middleware/replaceString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
var hijackResponse = require('hijackresponse');
var escapeStringRegexp = require('escape-string-regexp');

module.exports = function (replacements) {
replacements = replacements || {};

return function injectLiveStyleScriptIncludeIntoHtml(req, res, next) {

if (req.get('X-Requested-With') !== 'XMLHttpRequest' && req.method === 'GET' && (req.accepts('html') || (req.headers.accept && req.headers.accept.indexOf('*/*') !== -1))) {
// Prevent If-None-Match revalidation with the downstream middleware with ETags that aren't suffixed with "-livestyle":
var ifNoneMatch = req.headers['if-none-match'];
if (ifNoneMatch) {
var validIfNoneMatchTokens = ifNoneMatch.split(" ").filter(function (etag) {
return /-(?:livestyle|processimage|compiless|compile-sass)\"$/.test(etag);
});
if (validIfNoneMatchTokens.length > 0) {
req.headers['if-none-match'] = validIfNoneMatchTokens.join(" ");
} else {
delete req.headers['if-none-match'];
}
}
delete req.headers['if-modified-since'];
delete req.headers['content-length'];
delete req.headers['accept-encoding'];

hijackResponse(res, function (err, res) {
if (err) {
return res.unhijack(function () {
next(err);
});
}
var contentType = res.getHeader('content-type');
if (!contentType || !contentType.match(/^text\/html(?:;\s*charset=('|"|)([^'"]+)\1)?$/i)) {
res.unhijack(true);
} else {

res.removeHeader('content-length');
res.removeHeader('last-modified');
res.writeHead(res.statusCode);

var chunks = [];
res.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function () {
var htmlStr = Object.keys(replacements).reduce(function (html, needle) {
return html.replace(new RegExp(escapeStringRegexp(needle), 'gi'), replacements[needle]);
}, chunks.join(''));

res.end(htmlStr);
});
}
});
}
next();
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"dependencies": {
"errorhandler": "1.4.2",
"escape-string-regexp": "^1.0.3",
"express": "4.13.3",
"express-autoprefixer": "4.0.3",
"express-compile-sass": "^3.0.4",
Expand Down