var http = require('http') , spawn = require('child_process').spawn , url = require('url') , path = require('path') , fs = require('fs') var filed = require('filed') , LiveReloadServer = require('live-reload') , response_stream = require('response-stream') , optimist = require('optimist').argv , colors = require('colors') , through = require('through') var help = require('./help') , fake_index_html = fs.readFileSync(path.join(__dirname, 'fake_index.html'), 'utf8') var argv = process.argv.slice(/node/.test(process.argv[0]) ? 2 : 1) , CWD = process.cwd() , browserify_path = which_browserify() , browserify_args = null , ENTRY_POINT_TARGET , ENTRY_POINT_URL , LIVE_PORT , PORT if(!get_args()) { return process.exit(1) } info('using '+browserify_path.replace(CWD, '.')) var RESPONSE_MAP = [ 'grey' , 'grey' , 'green' , 'magenta' , 'yellow' , 'red' ] http.createServer(function(req, resp) { var parsed = url.parse(req.url, true) , pathname = parsed.pathname.slice(1) || 'index.html' , filepath = path.resolve(path.join(CWD, pathname)) , logged_pathname = '/'+pathname , logged_color = null , query = parsed.query || {} , start = Date.now() , bytesize = 0 , stream , args , bfy if(pathname === ENTRY_POINT_URL || 'browserify' in query) { args = [ pathname === ENTRY_POINT_URL ? ENTRY_POINT_TARGET : filepath ].concat(browserify_args) logged_pathname = logged_pathname + ' -> ' + [browserify_path] .concat(args) .map(function(xxx) { return xxx.replace(CWD, '.') }) .join(' ').magenta bfy = spawn(browserify_path, args) stream = response_stream(bfy.stdout) logged_color = 'underline' stream.setHeader('content-type', 'text/javascript') bfy.stderr.pipe(process.stdout) var error = [] bfy.stderr.on('data', [].push.bind(error)) bfy.stderr.on('end', function() { bfyerror(error.join('')) }) } else if(fs.existsSync(filepath)) { stream = fs.createReadStream(filepath) } else if(/html/.test(req.headers.accept || '')) { logged_pathname = logged_pathname.blue + ' ' + '(generated)'.grey stream = response_stream(fake_index(query)) stream.setHeader('content-type', 'text/html') } else { stream = response_stream(through()) stream.writeHead(404, {'content-type': 'text/plain'}) process.nextTick(function() { stream.end('not found') }) } stream.pipe(resp) stream.on('end', log) stream.on('data', function(data) { bytesize += data.length }) function log() { var code = resp.statusCode + '' console.log( code[RESPONSE_MAP[code.charAt(0)]] + ' ' + pad(Date.now() - start + 'ms', 6) + ' ' + pad(sized(bytesize), 9).grey + ' ' + logged_pathname ) } function bfyerror(data) { resp.end('('+function(error) { var pre = document.createElement('pre') pre.textContent = error document.body.children.length ? document.body.insertBefore(pre, document.body.children[0]) : document.body.appendChild(pre) }+'('+JSON.stringify(data+'')+'))') } }).listen(PORT) if(optimist.live) { LIVE_PORT = optimist.live === true ? 9967 : optimist.live LiveReloadServer({ port: LIVE_PORT }) } function fake_index(query) { var stream = through() , index_path , live_text , html index_path = query.p || ENTRY_POINT_URL.replace(CWD, '') if(query.p) { index_path += '?browserify' } live_text = '' html = fake_index_html .replace('{{ PATH }}', index_path) .replace('{{ EXTRA }}', LIVE_PORT ? live_text : '') process.nextTick(function() { stream.end(html) }) return stream } function get_args() { if(!argv.length || optimist.h || optimist.help) { return help() } for(var i = 0, len = argv.length; i < len; ++i) { if(argv[i] === '--') { break } } browserify_args = argv.splice(i+1, argv.length - i) argv[0] = argv[0].split(':') ENTRY_POINT_TARGET = argv[0][0] ENTRY_POINT_URL = argv[0][~~(1 % argv[0].length)] ENTRY_POINT_TARGET = path.resolve( path.join(CWD, ENTRY_POINT_TARGET) ) ENTRY_POINT_URL = ENTRY_POINT_URL.replace(/^\.\//g, '') PORT = +argv[1] || 9966 info('listening on '+PORT) return true } function which_browserify() { if(optimist.browserify) { return optimist.browserify } var local = path.join(CWD, 'node_modules/.bin/browserify') if(fs.existsSync(local)) { return local } return 'browserify' } function info(what) { console.log(what.grey) } function sized(bytesize) { var powers = ['B', 'KB', 'MB', 'GB'] , curr , next for(var i = 0, len = powers.length; i < len; ++i) { curr = Math.pow(1024, i) next = Math.pow(1024, i + 1) if(bytesize < next) { return (bytesize / curr).toFixed(2).replace(/\.?0+$/g, '') + powers[i] } } return (bytesize / curr) + 'gib' } function pad(s, n, w) { while(s.length < n) { s = (w || ' ') + s } return s }