From 3ebe2cfb80f9c54e9b643c4ef4fb4ba7c3bd7372 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 21 Feb 2015 18:52:48 -0500 Subject: [PATCH 001/302] udpating readme [ci skip] --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 997977b..52f7d8c 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,9 @@ Options: [![screenshot](http://i.imgur.com/LJP7d9I.png)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) -The original motivation for making budō was to build a simple tool around Chrome Script Injection. This has since split off into its own repository: [budo-chrome](https://github.com/mattdesl/budo-chrome) to maintain a small scope for budō. - [(click for demo)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) +The original motivation for making budō was to build a simple tool around Chrome Script Injection. This has since split off into its own repository: [budo-chrome](https://github.com/mattdesl/budo-chrome) to minimize the scope of budō. ## License From 423299d46e60e9fef50b95881c794df49b4c73d0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 21 Feb 2015 18:53:57 -0500 Subject: [PATCH 002/302] 1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b56e1fc..032601f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "0.1.12", + "version": "1.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 978e2c8c3077bd67b1cd1befe53f04be0f1bfa6b Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Feb 2015 17:01:04 -0500 Subject: [PATCH 003/302] fix broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52f7d8c..bd3498a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ See [docs](#docs) for more features. PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/caspervonb) for the early groundwork. -## about +## docs - [basic usage](docs/basics.md) - [comparisons](docs/comparisons.md) From a45db79304a6acb441580094b8eee31ef7c7020d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Feb 2015 17:01:09 -0500 Subject: [PATCH 004/302] 1.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 032601f..687dae8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.0.0", + "version": "1.0.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 6865365c2e6b1466e3876e6824338dc501fd731d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 27 Feb 2015 15:23:13 -0500 Subject: [PATCH 005/302] update comparisons --- docs/comparisons.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/comparisons.md b/docs/comparisons.md index 31657ca..6776afe 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -6,7 +6,7 @@ ## beefy -[Beefy](https://github.com/chrisdickinson/beefy) is a feature-rich dev tool for browserify, and much of the inspiration for this project. However, it currently has some shortcomings[[1]](https://github.com/chrisdickinson/beefy/issues/49)[[2]](https://github.com/chrisdickinson/beefy/issues/63) and often feels clunky to deliver as a local dependency. It also takes a different approach to bundling, by using watchify's programmatic API rather than [execspawn](https://www.npmjs.com/package/npm-execspawn). +[Beefy](https://github.com/chrisdickinson/beefy) is a feature-rich dev tool for browserify, and much of the inspiration for this project. It has a wide scope, encompassing browserify and watchify, and takes a different approach to bundling by using watchify's programmatic API rather than [execspawn](https://www.npmjs.com/package/npm-execspawn). ```sh #example ... @@ -15,9 +15,9 @@ beefy index.js --open ## wzrd -[wzrd](https://github.com/maxogden/wzrd) is a tiny spin-off of beefy that is ideal for [local dependencies](https://github.com/stackgl/learning-webgl-03/blob/db8f36a534b2a184924f8b890014ff3dd9a5b391/package.json#L6-L9). It introduces some novel ideas like entry mapping and ndjson output with tools like [garnish](https://github.com/mattdesl/garnish). +[wzrd](https://github.com/maxogden/wzrd) is a tiny spin-off of beefy that is ideal for [local dependencies](https://github.com/stackgl/learning-webgl-03/blob/db8f36a534b2a184924f8b890014ff3dd9a5b391/package.json#L6-L9). It has a small and focused scope, and encourages composition and diversity with other tools (e.g. [garnish](https://github.com/mattdesl/garnish) for pretty-printing). -However, incremental bundling is likely outside of its scope. +Incremental bundling is likely outside of its scope. ```sh #example ... From 944689d3e8624a274fbed8ffab88797a0c9971f9 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 27 Feb 2015 18:21:28 -0500 Subject: [PATCH 006/302] remove unused dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 687dae8..4e43af9 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", - "script-injector": "^0.1.7", "through2": "^0.6.3", "tmp": "0.0.24", "wtch": "^3.1.0", From 95b9a13726f071fbe9aa7b445538f60219b1e912 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 27 Feb 2015 18:21:32 -0500 Subject: [PATCH 007/302] 1.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e43af9..bed94f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.0.1", + "version": "1.0.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 2339b039eb98dff074cedfd95f1b2313d4e4228f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Feb 2015 18:39:02 -0500 Subject: [PATCH 008/302] gracefully close when browserify cannot bundle --- lib/budo.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/budo.js b/lib/budo.js index 1ad9f86..480e2b4 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -52,7 +52,11 @@ module.exports = function(watchifyArgs, opt) { server.close() emitter.emit('exit') } - + + //when watchify ends, close the server + watchProc.stderr.on('end', function() { + emitter.close() + }) return emitter function setOutfile(arglist, file) { From 5c8f79a0c18f591947ededaa37ac864829d8dada Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Feb 2015 18:39:14 -0500 Subject: [PATCH 009/302] 1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bed94f3..15f0190 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.0.2", + "version": "1.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From ef8041293b8d2698b6c24b720191aa37b2864906 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 4 Mar 2015 13:44:17 -0500 Subject: [PATCH 010/302] add the ability to specify an outfile during temp dir usage --- example/index.html | 2 +- example/other.html | 11 ++++ index.js | 21 +++++++- lib/get-output.js | 8 +-- lib/server.js | 1 - package.json | 3 +- test/test-simple.js | 121 ++++++++++++++++++++++++-------------------- 7 files changed, 106 insertions(+), 61 deletions(-) create mode 100644 example/other.html diff --git a/example/index.html b/example/index.html index ac946c8..17ef952 100644 --- a/example/index.html +++ b/example/index.html @@ -1,11 +1,11 @@ - budo + \ No newline at end of file diff --git a/example/other.html b/example/other.html new file mode 100644 index 0000000..a5a74a1 --- /dev/null +++ b/example/other.html @@ -0,0 +1,11 @@ + + + + + + budo + + + + + \ No newline at end of file diff --git a/index.js b/index.js index f845e41..1a048bb 100644 --- a/index.js +++ b/index.js @@ -20,7 +20,9 @@ module.exports = function(args, cb) { argv.port = argv.port || 9966 argv.dir = argv.dir || process.cwd() - getOutput(argv, function(err, output) { + + var outOpts = xtend(argv, { __to: entryMapping() }) + getOutput(outOpts, function(err, output) { if (err) { console.error("Error: Could not create temp bundle.js directory") process.exit(1) @@ -49,4 +51,21 @@ module.exports = function(args, cb) { }) }) }) + + function entryMapping() { + var mapTo + var first = argv._[0] + var parts = first.split(':') + if (parts.length > 1) { + var from = parts[0] + var to = parts[1] + argv._[0] = from + //clean up original arguments for watchify + var idx = args.indexOf(first) + if (idx>=0) + args[idx] = from + mapTo = to + } + return mapTo + } } \ No newline at end of file diff --git a/lib/get-output.js b/lib/get-output.js index c7da330..92030a3 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -5,15 +5,17 @@ var tmpdir = require('./tmpdir') module.exports = function getOutput(argv, cb) { var outfile = argv.o || argv.outfile if (!outfile) { - var to = 'bundle.js' + //user can specify a mapping for temp dirs + var bundleTo = argv.__to || 'bundle.js' + tmpdir(function(err, filedir) { var output if (!err) { - var file = path.join(filedir, to) + var file = path.join(filedir, bundleTo) output = { tmp: true, from: file, - to: to, + to: bundleTo, dir: filedir } } diff --git a/lib/server.js b/lib/server.js index de018cc..450edff 100644 --- a/lib/server.js +++ b/lib/server.js @@ -24,7 +24,6 @@ module.exports.static = function(opts) { var entryHandler = staticHandler if (out.tmp) entryHandler = ecstatic({ root: out.dir }) - router.addRoute('/' + out.to, function(req, res, params) { log.info({ url: req.url, type: 'static' }) entryHandler(req, res) diff --git a/package.json b/package.json index 15f0190..7114abb 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "test": "node test/test-simple.js | tap-spec", "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish -v", - "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish -v" + "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish -v", + "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish -v" }, "keywords": [ "browserify", diff --git a/test/test-simple.js b/test/test-simple.js index 938f7c3..d8c1b7f 100644 --- a/test/test-simple.js +++ b/test/test-simple.js @@ -41,14 +41,22 @@ test('should run on available port', function(t) { }) }) -// TODO: fix this case -// test('should get a bundle.js', function(t) { -// var cwd = path.resolve(__dirname, '..') -// runBundleMatch(t, { -// watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], -// budo: ['app.js'] -// }) -// }) +test('should get a bundle.js', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + budo: ['app.js'], + }) +}) + +test('entry mapping to bundle2.js', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app', '-v', '-o', 'bundle-expected.js'], + budo: ['app:bundle2.js'], + to: 'bundle2.js' + }) +}) test('should get a bundle.js with --outfile', function(t) { var cwd = path.resolve(__dirname, '..') @@ -67,14 +75,59 @@ test('should get a bundle.js with --dir', function(t) { }) }) +test('should create and destroy tmpdir', function(t) { + t.plan(2) + var proc = spawn(cliPath, ['app.js'], { cwd: __dirname, env: process.env }) + var expected = 'temp directory created at ' + proc.stdout.pipe(ndjson.parse()) + .on('data', function(data) { + if (data.level !== 'debug') + return + + var msg = data && data.message + var idx = msg.indexOf(expected) + + if (idx === -1) { + t.fail('no temp dir created') + kill(proc.pid) + } else { + var path = msg.substring(idx+expected.length).trim() + t.ok(true, 'created tmp dir') + proc.on('exit', cleanup(path)) + kill(proc.pid, 'SIGINT') + } + }) + .on('error', function(err) { + t.fail(err) + kill(proc.pid) + }) + + function cleanup(path) { + return function() { + fs.exists(path, function(exists) { + if (exists) { + t.fail('tmpdir not cleaned up '+path) + rimraf(path, function(err) { + if (err) + console.error(err) + }) + } + else t.ok(true, 'tmpdir cleaned up') + }) + } + } +}) + function runBundleMatch(t, opt) { opt = opt||{} t.plan(1) + t.timeoutAfter(10000) var cwd = opt.cwd || __dirname var foundMsg = false var bundle = path.join(__dirname, 'bundle.js') - var bundleExpected = path.join(__dirname, 'bundle-expected.js') + var outputFile = 'bundle-expected.js' + var bundleExpected = path.join(__dirname, outputFile) //the expected bundle var watchifyProc = npmSpawn('watchify '+ opt.watchify.join(' '), { cwd: cwd, env: process.env }) @@ -82,7 +135,7 @@ function runBundleMatch(t, opt) { watchifyProc.stdout.on('data',watchifyDone) function watchifyDone(msg) { - var suc = msg.toString().indexOf('bundle-expected.js') + var suc = msg.toString().indexOf(outputFile) if (suc === -1) t.fail('watchify process gave unexpected stdout/stderr message' ) kill(watchifyProc.pid) @@ -100,6 +153,9 @@ function runBundleMatch(t, opt) { proc.stdout.pipe(ndjson.parse()) .on('data', function(data) { var msg = (data.message||'').toLowerCase() + if (msg.indexOf('temp directory created') >= 0) + return + var running = 'server running at ' var idx = msg.indexOf(running) if (idx >= 0) { @@ -107,7 +163,7 @@ function runBundleMatch(t, opt) { setTimeout(function() { //let bundling finish var serverUrl = msg.substring(idx+running.length) request.get({ - uri: serverUrl + '/bundle.js' + uri: serverUrl + (opt.to || 'bundle.js') }, function(err, resp, data) { t.equal(data, source, 'bundle matches') kill(proc.pid) @@ -129,46 +185,3 @@ function runBundleMatch(t, opt) { }) } } - -test('should create and destroy tmpdir', function(t) { - t.plan(2) - var proc = spawn(cliPath, ['app.js'], { cwd: __dirname, env: process.env }) - var expected = 'temp directory created at ' - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - if (data.level !== 'debug') - return - - var msg = data && data.message - var idx = msg.indexOf(expected) - - if (idx === -1) { - t.fail('no temp dir created') - kill(proc.pid) - } else { - var path = msg.substring(idx+expected.length).trim() - t.ok(true, 'created tmp dir') - proc.on('exit', cleanup(path)) - kill(proc.pid, 'SIGINT') - } - }) - .on('error', function(err) { - t.fail(err) - kill(proc.pid) - }) - - function cleanup(path) { - return function() { - fs.exists(path, function(exists) { - if (exists) { - t.fail('tmpdir not cleaned up '+path) - rimraf(path, function(err) { - if (err) - console.error(err) - }) - } - else t.ok(true, 'tmpdir cleaned up') - }) - } - } -}) \ No newline at end of file From 47bbf8ddfac14a81e29f418af7069caa5fe45b6a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 4 Mar 2015 13:46:47 -0500 Subject: [PATCH 011/302] add docs on entry mapping --- docs/basics.md | 6 ++++++ index.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index e4ba15e..116e037 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -71,6 +71,12 @@ If you don't specify an `--outfile` or `-o` argument, budō will save a `bundle. budo index.js --verbose | garnish ``` +You can also change the mapping like so, which allows you to specify a different entry point in your `index.html`: + +```sh +budo index.js:static.js | garnish +``` + This is good for quick prototyping, but the `--outfile` approach is more robust and cross-platform, and thus preferred when delivering budō as a local dependency. ## live reload diff --git a/index.js b/index.js index 1a048bb..4bef140 100644 --- a/index.js +++ b/index.js @@ -56,7 +56,7 @@ module.exports = function(args, cb) { var mapTo var first = argv._[0] var parts = first.split(':') - if (parts.length > 1) { + if (parts.length > 1 && parts[1].length > 0) { var from = parts[0] var to = parts[1] argv._[0] = from From bf536c9186ca4a1e9925260f4dd1af19de6a8678 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 4 Mar 2015 13:46:50 -0500 Subject: [PATCH 012/302] 1.2.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7114abb..e3aa08d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.1.0", + "version": "1.2.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { @@ -53,7 +53,7 @@ "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish -v", - "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish -v" + "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish -v" }, "keywords": [ "browserify", From 3132186e113db366b04153852501f931c18e900a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Mar 2015 09:00:13 -0400 Subject: [PATCH 013/302] update docs --- docs/comparisons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/comparisons.md b/docs/comparisons.md index 6776afe..f818374 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -2,7 +2,7 @@ ## budō -[budō](https://github.com/mattdesl/budo) lies somewhere between the rich feature set of [Beefy](#beefy) and the small focus of [wzrd](#wzrd). It spawns a watchify process, produces ndjson logs, and includes some more experimental features for live-reloading, [script injection](https://github.com/mattdesl/budo-chrome), and rapid prototyping. +[budō](https://github.com/mattdesl/budo) lies somewhere between the rich feature set of [Beefy](#beefy) and the small focus of [wzrd](#wzrd). It spawns a watchify process, produces ndjson logs, and integrates with LiveReload (including CSS injection). It is also the base for more experimental features like [Chrome script injection](https://github.com/mattdesl/budo-chrome), and rapid prototyping. ## beefy From 3fce6c3594d7aae27fbeb57f724609a9c00ad3d0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Mar 2015 10:42:36 -0400 Subject: [PATCH 014/302] supporting "index.html" route with live reload --- lib/server.js | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/server.js b/lib/server.js index 450edff..bdeefd5 100644 --- a/lib/server.js +++ b/lib/server.js @@ -19,25 +19,41 @@ module.exports.static = function(opts) { var router = Router() var live = opts.live + var host = opts.host + var livePort = opts['live-port'] + + var liveOpts = { + host: opts.host, + port: opts['live-port'] + } var out = opts.output var entryHandler = staticHandler if (out.tmp) entryHandler = ecstatic({ root: out.dir }) + router.addRoute('/' + out.to, function(req, res, params) { log.info({ url: req.url, type: 'static' }) entryHandler(req, res) }) - router.addRoute('/', function(req, res, params) { + router.addRoute('/index.html', home) + router.addRoute('/', home) + + router.addRoute('*', function(req, res, params) { + // if (live) + // res = inject(res, liveOpts) + log.info({ url: req.url, type: 'static' }) + staticHandler(req, res) + }) + + return router + + function home(req, res, params) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed - if (live) - res = inject(res, { - host: opts.host, - port: opts['live-port'] - }) - + if (live) + res = inject(res, liveOpts) var type = exists ? 'static' : 'generated' log.info({ url: req.url, type: type }) @@ -46,14 +62,7 @@ module.exports.static = function(opts) { else generateIndex(out.to, req, res) }) - }) - - router.addRoute('*', function(req, res, params) { - log.info({ url: req.url, type: 'static' }) - staticHandler(req, res) - }) - - return router + } function generateIndex(outfile, req, res) { res.setHeader('content-type', 'text/html') From be9bc69792415dcbc844fc09e7e21339e8ecd057 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Mar 2015 10:42:55 -0400 Subject: [PATCH 015/302] 1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3aa08d..0032218 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.2.0", + "version": "1.2.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 555557a9e496fc62f30cbae739dc5aab7de7a332 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Mar 2015 10:44:00 -0400 Subject: [PATCH 016/302] update stability badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd3498a..e7e1810 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # budō -[![unstable](http://badges.github.io/stability-badges/dist/unstable.svg)](http://github.com/badges/stability-badges) +[![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but with a stronger focus on incremental bundling, LiveReload (including CSS injection), and other [experimental features](#script-injection) down the road. From a8b72ed93f88415aac29550b01b53a17b072eda3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Mar 2015 10:44:06 -0400 Subject: [PATCH 017/302] 1.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0032218..e4cb4c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.2.1", + "version": "1.2.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From db3b3820a78f2b03aa6c110ca1e5c32b07ff2e63 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 13 Mar 2015 10:29:43 -0400 Subject: [PATCH 018/302] udpating readme [ci skip] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e7e1810..8d2d448 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/cas - [comparisons](docs/comparisons.md) - [running tests and examples](docs/tests-and-examples.md) - [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) +- [rapid prototyping with budō and wzrd](http://mattdesl.svbtle.com/rapid-prototyping) ## usage From 2a4e1988ef1e6c4e0a6e8ae38bf71faf79d22bc2 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 13 Mar 2015 10:29:47 -0400 Subject: [PATCH 019/302] 1.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e4cb4c9..91d6971 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.2.2", + "version": "1.2.3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 7016af45b087f1667e739e54bbc69ccee46f1be2 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 18 Mar 2015 11:56:53 -0400 Subject: [PATCH 020/302] adding debug by default (+1 squashed commit) Squashed commits: [9a739db] fixing various edge cases for debug --- README.md | 2 ++ lib/budo.js | 40 +++++++++++++++++++++++++++++++++++++++- test/test-simple.js | 16 ++++++++++++---- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8d2d448..4eef34d 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Options: --live-port the LiveReload port, default 35729 ``` +By default, the `--debug` option will be sent to watchify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. + ## Script Injection [![screenshot](http://i.imgur.com/LJP7d9I.png)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) diff --git a/lib/budo.js b/lib/budo.js index 480e2b4..00a2fac 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -14,8 +14,10 @@ module.exports = function(watchifyArgs, opt) { var emitter = new Emitter() - //patch watchify args with new outfile + //patch watchify args with new outfile and default debug setOutfile(watchifyArgs, output.from) + setDebug(watchifyArgs, opt.d || opt.debug) + //spin up watchify instance var watchProc = watchify(watchifyArgs) @@ -69,4 +71,40 @@ module.exports = function(watchifyArgs, opt) { } else arglist[idx+1] = file } + + //Supports various debug features + // ['--no-debug'] (from minimist parsing, which browserify uses) + // ['--debug=false'] + // ['--debug', 'false'] + // ['-d', 'false'] + //Need to remove the debug flag entirely from args otherwise browserify + //will use source maps + function setDebug(arglist, enabled) { + enabled = enabled !== false && enabled !== 'false' + + //user can disable debug + if (enabled === false) { + removeDebug(arglist) + } + //by default, we want to enable debug + else { + var idx1 = arglist.indexOf('-d') + var idx2 = arglist.indexOf('--debug') + if (idx1 === -1 && idx2 === -1) + arglist.push('-d') + } + } + + function removeDebug(arglist) { + var args = ['-d', '--debug', '--debug=false'] + args.forEach(function(arg) { + var idx = arglist.indexOf(arg) + if (idx === -1 || String(arglist[idx+1]) === 'true') + return + if (String(arglist[idx+1]) === 'false') + arglist.splice(idx, 2) + else + arglist.splice(idx, 1) + }) + } } \ No newline at end of file diff --git a/test/test-simple.js b/test/test-simple.js index d8c1b7f..df55071 100644 --- a/test/test-simple.js +++ b/test/test-simple.js @@ -44,7 +44,7 @@ test('should run on available port', function(t) { test('should get a bundle.js', function(t) { var cwd = path.resolve(__dirname, '..') runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], budo: ['app.js'], }) }) @@ -52,7 +52,7 @@ test('should get a bundle.js', function(t) { test('entry mapping to bundle2.js', function(t) { var cwd = path.resolve(__dirname, '..') runBundleMatch(t, { - watchify: ['app', '-v', '-o', 'bundle-expected.js'], + watchify: ['app', '-v', '-o', 'bundle-expected.js', '-d'], budo: ['app:bundle2.js'], to: 'bundle2.js' }) @@ -61,16 +61,24 @@ test('entry mapping to bundle2.js', function(t) { test('should get a bundle.js with --outfile', function(t) { var cwd = path.resolve(__dirname, '..') runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], budo: ['app.js', '-o', 'bundle.js'] }) }) +test('should disable source maps with --no-debug', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + budo: ['app.js', '-o', 'bundle.js', '--no-debug'] + }) +}) + test('should get a bundle.js with --dir', function(t) { var cwd = path.resolve(__dirname, '..') runBundleMatch(t, { cwd: cwd, - watchify: ['test/app.js', '-v', '-o', 'test/bundle-expected.js'], + watchify: ['test/app.js', '-v', '-o', 'test/bundle-expected.js', '-d'], budo: ['test/app.js', '-o', 'bundle.js', '--dir', 'test'] }) }) From 1aa1015bd6cc925ace1279e9436e149092b624fc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 18 Mar 2015 12:15:01 -0400 Subject: [PATCH 021/302] 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91d6971..42fa999 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.2.3", + "version": "1.3.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 286ca14e583657874e2f30c7b4e78bd920f95789 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 22 Mar 2015 21:22:22 -0400 Subject: [PATCH 022/302] wip error docs --- README.md | 1 + docs/errors.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 docs/errors.md diff --git a/README.md b/README.md index 4eef34d..53f98a8 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/cas - [basic usage](docs/basics.md) - [comparisons](docs/comparisons.md) +- [error reporting](docs/errors.md) - [running tests and examples](docs/tests-and-examples.md) - [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) - [rapid prototyping with budō and wzrd](http://mattdesl.svbtle.com/rapid-prototyping) diff --git a/docs/errors.md b/docs/errors.md new file mode 100644 index 0000000..21a3acc --- /dev/null +++ b/docs/errors.md @@ -0,0 +1,17 @@ +# errorify plugin + +By default, bundle errors will be printed to `stdout` (terminal) and also injected into the browser console[[1]](https://github.com/substack/watchify/blob/ffaf7ec048905f707ba1876579dc7082f1d50de5/bin/cmd.js#L27-L29) +. + +For clearer error reporting, you can use the [errorify](https://github.com/zertosh/errorify) plugin. This will write the error to the DOM as well as console. + +``` +budo index.js --live -p errorify | garnish +``` + +Some tools like [babelify](https://www.npmjs.com/package/babelify) can also improve the error messages. + +![enhanced](http://i.imgur.com/Q4DLQBQ.png) + +The above uses babelify and the following CSS style: + From fb01c62cc1a099f2a59078791693f840b4e67e81 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 22 Mar 2015 21:23:49 -0400 Subject: [PATCH 023/302] wip error docs --- docs/errors.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/errors.md b/docs/errors.md index 21a3acc..91f93c0 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -1,17 +1,20 @@ -# errorify plugin +# error reporting By default, bundle errors will be printed to `stdout` (terminal) and also injected into the browser console[[1]](https://github.com/substack/watchify/blob/ffaf7ec048905f707ba1876579dc7082f1d50de5/bin/cmd.js#L27-L29) . For clearer error reporting, you can use the [errorify](https://github.com/zertosh/errorify) plugin. This will write the error to the DOM as well as console. -``` +```sh budo index.js --live -p errorify | garnish ``` -Some tools like [babelify](https://www.npmjs.com/package/babelify) can also improve the error messages. - ![enhanced](http://i.imgur.com/Q4DLQBQ.png) -The above uses babelify and the following CSS style: +The above uses [babelify](https://www.npmjs.com/package/babelify) (which further improves the error message) and a custom CSS style for errorify, + + +```sh +budo index.js --live -t babelify -p errorify | garnish +``` From b7efab094b34a9c49308254ad952c2b77f98ed1c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 22 Mar 2015 21:32:07 -0400 Subject: [PATCH 024/302] adding error notes --- docs/errors.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/errors.md b/docs/errors.md index 91f93c0..6158d32 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -1,20 +1,27 @@ # error reporting -By default, bundle errors will be printed to `stdout` (terminal) and also injected into the browser console[[1]](https://github.com/substack/watchify/blob/ffaf7ec048905f707ba1876579dc7082f1d50de5/bin/cmd.js#L27-L29) +By default, bundle errors will be printed to `stdout` (terminal) and also printed to the browser console on reload[[1]](https://github.com/substack/watchify/blob/ffaf7ec048905f707ba1876579dc7082f1d50de5/bin/cmd.js#L27-L29) . -For clearer error reporting, you can use the [errorify](https://github.com/zertosh/errorify) plugin. This will write the error to the DOM as well as console. +For clearer error reporting, you can use the [errorify](https://github.com/zertosh/errorify) plugin. This will write the error to the DOM with the problematic file and line number. ```sh +# install +npm install errorify --save-dev + +# include it with --plugin or -p budo index.js --live -p errorify | garnish ``` -![enhanced](http://i.imgur.com/Q4DLQBQ.png) +Using [babelify](https://www.npmjs.com/package/babelify) and errorify together, the error message looks like this: -The above uses [babelify](https://www.npmjs.com/package/babelify) (which further improves the error message) and a custom CSS style for errorify, +![enhanced](http://i.imgur.com/Q4DLQBQ.png) +It uses the following CSS style: -```sh -budo index.js --live -t babelify -p errorify | garnish +```css +body > .errorify { + color: red; + padding: 5px 10px; +} ``` - From fbce5bbf19e5cd6c00efc722e0fd1c85719d380b Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 22 Mar 2015 21:32:33 -0400 Subject: [PATCH 025/302] 1.3.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42fa999..3ee0477 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.3.0", + "version": "1.3.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 113ec6b5adc27a85487cefedd4bd561305f787eb Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 15:34:02 -0400 Subject: [PATCH 026/302] fixing space with entries --- lib/watchify.js | 3 ++- package.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/watchify.js b/lib/watchify.js index d29598b..50e8f8b 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,9 +1,10 @@ var spawn = require('npm-execspawn') +var quote = require('shell-quote').quote //Runs watchify with given args, returns process module.exports = function(watchifyArgs) { var cmd = ['watchify'] - .concat(watchifyArgs||[]) + .concat(quote(watchifyArgs||[])) .join(' ') var proc = spawn(cmd) diff --git a/package.json b/package.json index 3ee0477..40d29ba 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", + "shell-quote": "^1.4.3", "through2": "^0.6.3", "tmp": "0.0.24", "wtch": "^3.1.0", From dc95d7b6e49cf258f07279239d582e9e019422f6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 15:36:29 -0400 Subject: [PATCH 027/302] 1.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 40d29ba..e7d290d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.3.1", + "version": "1.3.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 45448c9c5c8fa1dc9923703c5a4477e9507d7bc1 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 22 Mar 2015 23:33:34 -0400 Subject: [PATCH 028/302] - refactor to support a programmatic API - add more tests - fix live-port bug code cleanup, bump to new watchify version (+13 squashed commits) Squashed commits: [c696116] fixing space with entries [fb8f6be] updating tests; fixing live-port bug; adding watch and reload to emitter [dfbf41b] udpating readme [ci skip] [ec5cced] update docs [3c40534] update live reload blob [a791929] updating API and docs to default host name [1355848] fixing lint errors; fixing entry mapping regression; getting rid of "output" and putting it into event object [d04497d] udpate docs [8d42116] adding docs [66fd52f] fixing ports and args [3c121c2] port moved to cmd now, api expects port to be known and available [c270702] using emitter for entry point now [e73a8a6] using dargs for arguments now --- README.md | 24 +++++ bin/cmd.js | 33 +++--- docs/programmatic-usage.md | 112 ++++++++++++++++++++ index.js | 109 +++++++++++-------- lib/budo.js | 135 +++++++++++------------- lib/server.js | 7 +- lib/watchify.js | 5 +- package.json | 8 +- test/cleanup.js | 17 +++ test/test-api.js | 34 ++++++ test/test-cli.js | 209 +++++++++++++++++++++++++++++++++++++ test/test-live.js | 88 ++++++++++++++++ test/test-simple.js | 195 ---------------------------------- 13 files changed, 635 insertions(+), 341 deletions(-) create mode 100644 docs/programmatic-usage.md create mode 100644 test/cleanup.js create mode 100644 test/test-api.js create mode 100644 test/test-cli.js create mode 100644 test/test-live.js delete mode 100644 test/test-simple.js diff --git a/README.md b/README.md index 53f98a8..9ffb8c8 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,15 @@ PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/cas - [error reporting](docs/errors.md) - [running tests and examples](docs/tests-and-examples.md) - [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) +- [programmatic usage (Gulp, Grunt)](docs/programmatic-usage.md) - [rapid prototyping with budō and wzrd](http://mattdesl.svbtle.com/rapid-prototyping) ## usage [![NPM](https://nodei.co/npm/budo.png)](https://www.npmjs.com/package/budo) +### CLI + Details for `budo` command-line interface. Other options like `--verbose` and `--transform` are sent to browserify/watchify. ```sh @@ -66,6 +69,27 @@ Options: By default, the `--debug` option will be sent to watchify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. +*Note:* The `--outfile` is relative to the specified `--dir`. + +### API + +The API mirrors the CLI except you must provide a `stream` for logging, and it does not attempt to auto-portfind. + + +```js +var budo = require('budo') + +budo('./src/index.js', { + live: true, //live reload + stream: process.stdout, //log to stdout + port: 8000 //use this port +}).on('connnect', function(ev) { + //... +}) +``` + +See [API usage](docs/programmatic-usage.md) for more details. + ## Script Injection [![screenshot](http://i.imgur.com/LJP7d9I.png)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) diff --git a/bin/cmd.js b/bin/cmd.js index 556d4da..fa764f3 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -3,25 +3,24 @@ //Starts up budo with some logging, //also listens for live reload events -require('bole').output({ - stream: process.stdout, - level: 'debug' -}) - var args = process.argv.slice(2) var opts = require('minimist')(args) -var wtch = require('wtch') +var portfinder = require('portfinder') -var defaultGlob = '**/*.{html,css}' +var entries = opts._ +opts.stream = process.stdout +delete opts._ -require('../')(args, function(budo) { - var watcher - if (opts.live || opts['live-plugin']) { - watcher = wtch([defaultGlob, budo.output.glob]) +portfinder.basePort = opts.port || opts.p || 9966 +portfinder.getPort(function(err, port) { + if (err) { + console.error("Could not find port", err) + process.exit(1) } - - budo.on('exit', function() { - if (watcher) - watcher.close() - }) -}) \ No newline at end of file + opts.port = port + require('../')(entries, opts) + .on('error', function(err2) { + console.error(err2.message) + process.exit(1) + }) +}) diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md new file mode 100644 index 0000000..a264fd1 --- /dev/null +++ b/docs/programmatic-usage.md @@ -0,0 +1,112 @@ +# API + +The API mirrors the CLI except you must provide a `stream` for logging, and it does not attempt to auto-portfind. + +```js +var budo = require('budo') + +budo('./src/index.js', { + live: true, //live reload + stream: process.stdout, //log to stdout + port: 8000 //use this port +}).on('connnect', function(ev) { + //... +}) +``` + +#### `b = budo(entry[, opts])` + +Spins up a new instance of `budo`, where `entry` is a path or or array of paths. + +The options are the same as CLI, except that the API does not attempt auto portfinding, and does not print to stdout. You can specify a `stream` option to print to. + +The return value is an event emitter. + +#### `b.on('exit')` + +Called when the server is closed. + +#### `b.on('error')` + +Called on an error. + +#### `b.on('connect')` + +Called once the budo server connects. The callback is passed an `event` object that looks like this: + +```js +{ + uri: 'http://localhost:9966/', //served URI + from: 'app/bundle.js', //the actual path to the bundle + to: 'bundle.js', //the mapping server expects + glob: 'app/bundle.js', //file glob for bundle.js + dir: 'app', //the working directory + host: 'localhost', //defaults to localhost + port: 9966, //the port we're running on + + //also provides a function to close the budo instance + close() +} +``` + +It's recommended to use `glob` instead of `from` if you intend to watch the `bundle.js` for file changes (e.g. determining when the bundle is ready to launch the browser). This is due to some issues with OSX temp dir file watching. + +# gulp & grunt + +Budo works without gulp and grunt, but you may want to wrap it within the same build environment for consistency. + +### simple recipe + +A simple case of budo within gulp might look like this: + +```js +var gulp = require('gulp') +var budo = require('budo') + +//start our local development server +gulp.task('dev', function(cb) { + budo('index.js') + .on('connect', function(app) { + console.log("Server started at "+app.uri) + }) + .on('exit', cb) +}) +``` + +Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and write `bundle.js` to a temp directory. + +### advanced recipes + +The following script shows how you can include a few more features to the task: + +- uses LiveReload on bundle change +- uses `babelify` for ES6 transpiling +- uses `errorify` to display syntax errors in the browser +- pretty-prints server requests to stdout with [garnish](https://github.com/mattdesl/garnish) + +```js +var gulp = require('gulp') +var budo = require('budo') +var garnish = require('garnish') + +//advanced example +gulp.task('default', function(cb) { + //using garnish for pretty-printing server requests + var pretty = garnish() + pretty.pipe(process.stdout) + + budo('index.js', { + live: true, //live reload + stream: pretty, //output stream + port: 8000, //the port to serve on + plugin: 'errorify', //nicer errors during dev + transform: 'babelify' //ES6 transpiling + }).on('exit', cb) +}) +``` + +Note that `babelify` and `errorify` need to be saved as local devDependencies. + +### demo + +[budo-gulp-starter](https://github.com/mattdesl/budo-gulp-starter) demonstrates some more complex applications of budo and gulp. \ No newline at end of file diff --git a/index.js b/index.js index 4bef140..fe1e8c3 100644 --- a/index.js +++ b/index.js @@ -1,71 +1,90 @@ -var log = require('bole')('budo') -var minimist = require('minimist') -var portfinder = require('portfinder') +var bole = require('bole') +var log = bole('budo') var xtend = require('xtend') var assign = require('xtend/mutable') - +var Emitter = require('events/') var getOutput = require('./lib/get-output') +var wtch = require('wtch') + +var defaultGlob = '**/*.{html,css}' var budo = require('./lib/budo') -var noop = function(){} -module.exports = function(args, cb) { - cb = cb||noop +module.exports = function(entry, opts) { + var argv = assign({}, opts) - var argv = minimist(args) - if (argv._.length === 0) { - console.error("No entry scripts specified!") - process.exit(1) + if (argv.stream) { + bole.output({ + stream: argv.stream, + level: 'debug' + }) } - - argv.port = argv.port || 9966 - argv.dir = argv.dir || process.cwd() + var emitter = new Emitter() + var watcher + + var entries = Array.isArray(entry) ? entry : [ entry ] + entries = entries.filter(Boolean) + if (entries.length === 0) { + bail("No entry scripts specified!") + return emitter + } + + argv.port = typeof argv.port === 'number' ? argv.port : 9966 + argv.dir = argv.dir || process.cwd() var outOpts = xtend(argv, { __to: entryMapping() }) + getOutput(outOpts, function(err, output) { if (err) { - console.error("Error: Could not create temp bundle.js directory") - process.exit(1) + bail("Error: Could not create temp bundle.js directory") + return emitter } - //determine next port - portfinder.basePort = argv.port - portfinder.getPort(function(err, port) { - if (err) { - console.error("Error: Could not get available port") - process.exit(1) - } - //run watchify server - var emitter = budo(args, xtend(argv, { - port: port, - output: output - })).on('error', function(err) { - console.error("Error running budo on", port, err) - process.exit(1) - }).on('exit', function() { + //run watchify server + var app = budo(entries, output, argv) + .on('error', function(err2) { + var msg = "Error running budo on " + argv.port + ': ' + err2 + bail(msg) + }) + .on('exit', function() { log.info('closing') + emitter.emit('exit') + if (watcher) + watcher.close() }) - - emitter.on('connect', function(result) { - cb(result) + .on('connect', function() { + //setup live reload + if (argv.live || argv['live-plugin']) { + var liveOptions = { + port: argv['live-port'] + } + + //dispatches watch and LiveReload events + watcher = wtch([defaultGlob, app.glob], liveOptions) + watcher.on('watch', emitter.emit.bind(emitter, 'watch')) + watcher.on('reload', emitter.emit.bind(emitter, 'reload')) + } + emitter.emit('connect', app) }) - }) }) + return emitter + function entryMapping() { - var mapTo - var first = argv._[0] + var to + var first = entries[0] var parts = first.split(':') if (parts.length > 1 && parts[1].length > 0) { var from = parts[0] - var to = parts[1] - argv._[0] = from - //clean up original arguments for watchify - var idx = args.indexOf(first) - if (idx>=0) - args[idx] = from - mapTo = to + to = parts[1] + entries[0] = from } - return mapTo + return to + } + + function bail(msg) { + process.nextTick(function() { + emitter.emit('error', new Error(msg)) + }) } } \ No newline at end of file diff --git a/lib/budo.js b/lib/budo.js index 00a2fac..1601fbe 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -1,55 +1,35 @@ var path = require('path') var Emitter = require('events/') var watchify = require('./watchify') -var minimist = require('minimist') var xtend = require('xtend') var assign = require('xtend/mutable') var http = require('./server').http var log = require('bole')('budo') +var dargs = require('dargs') + +module.exports = function(entries, output, opt) { + opt = opt||{} -module.exports = function(watchifyArgs, opt) { - var output = opt.output var port = opt.port || 9966 var host = opt.host + var serverOpt = xtend(opt, { output: output }) var emitter = new Emitter() - - //patch watchify args with new outfile and default debug - setOutfile(watchifyArgs, output.from) - setDebug(watchifyArgs, opt.d || opt.debug) - + var closed = false + //spin up watchify instance + var watchifyArgs = getWatchifyArgs(entries, output, opt) var watchProc = watchify(watchifyArgs) - var serverOpt = xtend(opt, { output: output }) var server = http(serverOpt) .on('error', function(err) { emitter.emit('error', err) }) - .listen(port, host, function(err) { - if (err) { - emitter.emit('error', new Error("Could not connect to server:", err)) - return - } - var uri = "http://"+(host||'localhost')+":"+port+"/" - log.info("Server running at", uri) - - //bug with chokidar@1.0.0-rc3 - //anything in OSX tmp dirs need a wildcard - //to work with fsevents - var glob = output.tmp - ? path.join(output.dir, '**.js') - : output.from - - //add the uri / output to budo instance - assign(emitter, { - uri: uri, - output: xtend(output, { glob: glob }) - }) - emitter.emit('connect', emitter) - }) + .listen(port, host, handler) emitter.close = function() { + if (closed) return + closed = true watchProc.kill() server.close() emitter.emit('exit') @@ -61,50 +41,59 @@ module.exports = function(watchifyArgs, opt) { }) return emitter - function setOutfile(arglist, file) { - var idx = arglist.indexOf('-o') - if (idx === -1) - idx = arglist.indexOf('--outfile') - if (idx === -1) { - arglist.push('-o') - arglist.push(file) - } else - arglist[idx+1] = file - } - - //Supports various debug features - // ['--no-debug'] (from minimist parsing, which browserify uses) - // ['--debug=false'] - // ['--debug', 'false'] - // ['-d', 'false'] - //Need to remove the debug flag entirely from args otherwise browserify - //will use source maps - function setDebug(arglist, enabled) { - enabled = enabled !== false && enabled !== 'false' - - //user can disable debug - if (enabled === false) { - removeDebug(arglist) - } - //by default, we want to enable debug - else { - var idx1 = arglist.indexOf('-d') - var idx2 = arglist.indexOf('--debug') - if (idx1 === -1 && idx2 === -1) - arglist.push('-d') + function handler(err) { + if (err) { + emitter.emit('error', new Error("Could not connect to server: " + err)) + return } + var hostname = (host||'localhost') + var uri = "http://"+hostname+":"+port+"/" + log.info("Server running at", uri) + + //bug with chokidar@1.0.0-rc3 + //anything in OSX tmp dirs need a wildcard + //to work with fsevents + var glob = output.tmp + ? path.join(output.dir, '**.js') + : output.from + + //add the uri / output to budo instance + assign(emitter, { + uri: uri, + port: port, + host: hostname, + server: server + }, output, { glob: glob }) + emitter.emit('connect', emitter) } +} - function removeDebug(arglist) { - var args = ['-d', '--debug', '--debug=false'] - args.forEach(function(arg) { - var idx = arglist.indexOf(arg) - if (idx === -1 || String(arglist[idx+1]) === 'true') - return - if (String(arglist[idx+1]) === 'false') - arglist.splice(idx, 2) - else - arglist.splice(idx, 1) - }) +function getWatchifyArgs(entries, output, opt) { + //do not mutate original opts + opt = assign({}, opt) + + //set output file + opt.outfile = output.from + delete opt.o + + //enable debug by default + if (opt.d !== false && opt.debug !== false) { + delete opt.d + opt.debug = true + } + //if user explicitly disabled debug... + else if (opt.d === false || opt.debug === false) { + delete opt.d + delete opt.debug } + + //clean up some possible collisions + delete opt.dir + delete opt.port + delete opt.host + delete opt.live + delete opt['live-port'] + delete opt['live-script'] + delete opt['live-plugin'] + return entries.concat(dargs(opt)) } \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index bdeefd5..7bd9c1b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -18,10 +18,7 @@ module.exports.static = function(opts) { var staticHandler = ecstatic(basedir) var router = Router() - var live = opts.live - var host = opts.host - var livePort = opts['live-port'] - + var live = opts.live || opts['live-script'] var liveOpts = { host: opts.host, port: opts['live-port'] @@ -41,8 +38,6 @@ module.exports.static = function(opts) { router.addRoute('/', home) router.addRoute('*', function(req, res, params) { - // if (live) - // res = inject(res, liveOpts) log.info({ url: req.url, type: 'static' }) staticHandler(req, res) }) diff --git a/lib/watchify.js b/lib/watchify.js index d29598b..de47480 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,11 +1,12 @@ var spawn = require('npm-execspawn') +var quote = require('shell-quote').quote //Runs watchify with given args, returns process module.exports = function(watchifyArgs) { var cmd = ['watchify'] - .concat(watchifyArgs||[]) + .concat(quote(watchifyArgs||[])) .join(' ') - + var proc = spawn(cmd) proc.stderr.on('data', function(err) { process.stderr.write(err.toString()) diff --git a/package.json b/package.json index 3ee0477..4113caa 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "bole": "^2.0.0", + "dargs": "^4.0.0", "ecstatic": "^0.5.8", "events": "^1.0.2", "inject-lr-script": "^1.0.0", @@ -23,9 +24,10 @@ "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", + "shell-quote": "^1.4.3", "through2": "^0.6.3", "tmp": "0.0.24", - "wtch": "^3.1.0", + "wtch": "^3.2.0", "xtend": "^4.0.0" }, "devDependencies": { @@ -45,11 +47,11 @@ "tape": "^3.5.0", "through2": "^0.6.3", "tree-kill": "0.0.6", - "watchify": "^2.3.0", + "watchify": "^2.6.2", "win-spawn": "^2.0.0" }, "scripts": { - "test": "node test/test-simple.js | tap-spec", + "test": "tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish -v", diff --git a/test/cleanup.js b/test/cleanup.js new file mode 100644 index 0000000..c4387dc --- /dev/null +++ b/test/cleanup.js @@ -0,0 +1,17 @@ +var rimraf = require('rimraf') +var path = require('path') + +module.exports = function cleanup(paths) { + setTimeout(function() { + var defaults = [ + path.join(__dirname, '/bundle.js'), + path.join(__dirname, '/.bundle.js') + ] + paths = paths || defaults + paths.forEach(function(file) { + rimraf(file, function(err) { + if (err) console.error(err) + }) + }) + }, 50) +} \ No newline at end of file diff --git a/test/test-api.js b/test/test-api.js new file mode 100644 index 0000000..422ece6 --- /dev/null +++ b/test/test-api.js @@ -0,0 +1,34 @@ +var test = require('tape') +var budo = require('../') +var cleanup = require('./cleanup') +var path = require('path') + +test('should provide an API', function(t) { + t.plan(8) + + budo('test/app.js', { + dir: __dirname, + port: 8000, + outfile: 'bundle.js' + }) + .on('error', function(err) { + t.fail(err) + }) + .on('connect', function(ev) { + var file = path.join(__dirname, 'bundle.js') + t.equal(ev.to, 'bundle.js', 'mapping matches') + t.equal(ev.from, file, 'from matches') + t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') + t.equal(ev.host, 'localhost', 'host is not specified') + t.equal(ev.port, 8000, 'port matches') + t.equal(ev.glob, file, 'glob matches file') + t.equal(ev.dir, __dirname, 'dir matches') + setTimeout(function() { + ev.close() + cleanup() + }, 1000) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) \ No newline at end of file diff --git a/test/test-cli.js b/test/test-cli.js new file mode 100644 index 0000000..ea7a8e8 --- /dev/null +++ b/test/test-cli.js @@ -0,0 +1,209 @@ +var rimraf = require('rimraf') +var fs = require('fs') +var path = require('path') +var spawn = require('win-spawn') +var npmSpawn = require('npm-execspawn') +var kill = require('tree-kill') + +var ndjson = require('ndjson') +var test = require('tape') +var concat = require('concat-stream') +var request = require('request') + +var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') + +// Some other tests needed: +// --live +// --live-plugin + +test('should fail without scripts', function(t) { + t.plan(1) + var proc = spawn(cliPath) + proc.stderr.pipe(concat(function(str) { + t.equal(str.toString().trim(), 'No entry scripts specified!') + })) +}) + +test('should run on available port', function(t) { + t.plan(1) + var proc = spawn(cliPath, ['app.js', '-o', 'bundle.js'], { + cwd: __dirname, + env: process.env + }) + var expected = 'Server running at' + + proc.stdout.pipe(ndjson.parse()) + .on('data', function(data) { + var msg = (data && data.message) || '' + t.ok(msg.indexOf(expected) >= 0, 'starts server') + kill(proc.pid) + }) + .on('error', function(err) { + t.fail(err) + kill(proc.pid) + }) +}) + +test('should get a bundle.js', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], + budo: ['app.js'], + }) +}) + +test('entry mapping to bundle2.js', function(t) { + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + budo: ['app.js:bundle.js', '--no-debug'], + to: 'bundle.js' + }) +}) + +test('should get a bundle.js with --outfile', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], + budo: ['app.js', '-o', 'bundle.js'] + }) +}) + +test('should disable source maps with --no-debug', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], + budo: ['app.js', '-o', 'bundle.js', '--no-debug'] + }) +}) + +test('should get a bundle.js with --dir', function(t) { + var cwd = path.resolve(__dirname, '..') + runBundleMatch(t, { + cwd: cwd, + watchify: ['test/app.js', '-v', '-o', 'test/bundle-expected.js', '-d'], + budo: ['test/app.js', '-o', 'bundle.js', '--dir', 'test'] + }) +}) + +test('should create and destroy tmpdir', function(t) { + t.plan(2) + //TODO: this test would be cleaner if done through API + t.timeoutAfter(6000) + var proc = spawn(cliPath, ['app.js'], { + cwd: __dirname, + env: process.env + }) + var expected = 'temp directory created at ' + proc.stdout.pipe(ndjson.parse()) + .on('data', function(data) { + if (data.level !== 'debug') + return + + var msg = data && data.message + var idx = msg.indexOf(expected) + + if (idx === -1) { + t.fail('no temp dir created') + kill(proc.pid) + } else { + var path = msg.substring(idx + expected.length).trim() + t.ok(true, 'created tmp dir') + proc.on('exit', cleanup(path)) + kill(proc.pid, 'SIGINT') + } + }) + .on('error', function(err) { + t.fail(err) + kill(proc.pid) + }) + + function cleanup(path) { + return function() { + fs.exists(path, function(exists) { + if (exists) { + t.fail('tmpdir not cleaned up ' + path) + rimraf(path, function(err) { + if (err) + console.error(err) + }) + } else t.ok(true, 'tmpdir cleaned up') + }) + } + } +}) + +function runBundleMatch(t, opt) { + opt = opt || {} + + t.plan(1) + t.timeoutAfter(10000) + var cwd = opt.cwd || __dirname + var foundMsg = false + var bundle = path.join(__dirname, 'bundle.js') + var outputFile = 'bundle-expected.js' + var bundleExpected = path.join(__dirname, outputFile) + + //the expected bundle + var watchifyProc = npmSpawn('watchify ' + opt.watchify.join(' '), { + cwd: cwd, + env: process.env + }) + watchifyProc.stderr.on('data', watchifyDone) + watchifyProc.stdout.on('data', watchifyDone) + + function watchifyDone(msg) { + var suc = msg.toString().indexOf(outputFile) + if (suc === -1) + t.fail('watchify process gave unexpected stdout/stderr message') + kill(watchifyProc.pid) + + fs.readFile(bundleExpected, 'utf8', function(err, data) { + if (err) + t.fail(err) + budoMatches(data) + }) + } + + function budoMatches(source) { + var proc = spawn(cliPath, opt.budo, { + cwd: cwd, + env: process.env + }) + proc.on('exit', cleanup) + proc.stderr.pipe(process.stderr) + proc.stdout.pipe(ndjson.parse()) + .on('data', function(data) { + var msg = (data.message || '').toLowerCase() + if (msg.indexOf('temp directory created') >= 0) + return + + var running = 'server running at ' + var idx = msg.indexOf(running) + if (idx >= 0) { + foundMsg = true + setTimeout(function() { //let bundling finish + var serverUrl = msg.substring(idx + running.length) + request.get({ + uri: serverUrl + (opt.to || 'bundle.js') + }, function(err, resp, data2) { + if (err) t.fail(err) + t.equal(data2, source, 'bundle matches') + kill(proc.pid) + }) + }, 1000) + } else if (!foundMsg) { + t.fail('no server running message in ' + msg) + kill(proc.pid) + } + }) + } + + function cleanup() { + rimraf(bundleExpected, function(err) { + if (err) console.error(err) + }) + rimraf(bundle, function(err) { + if (err) console.error(err) + }) + } +} \ No newline at end of file diff --git a/test/test-live.js b/test/test-live.js new file mode 100644 index 0000000..a437ad7 --- /dev/null +++ b/test/test-live.js @@ -0,0 +1,88 @@ +var test = require('tape') +var budo = require('../') +var path = require('path') +var request = require('request') +var cleanup = require('./cleanup') +var fs = require('fs') +var source = fs.readFileSync(path.join(__dirname, 'app.js'), 'utf8') + +test('should inject LiveReload snippet', function(t) { + t.plan(3) + t.timeoutAfter(10000) + + var server + + var entry = path.join(__dirname, 'app.js') + budo(entry, { + dir: __dirname, + port: 8000, + outfile: 'bundle.js', + live: true + }) + .on('error', function(err) { + t.fail(err) + }) + .on('watch', function(event, file) { + t.equal(path.basename(file), 'bundle.js', 'watch event triggered') + server.close() + cleanup() + }) + .on('connect', function(ev) { + server = ev + matchesHTML(t, ev.uri) + setTimeout(function() { + fs.writeFile(entry, source) + }, 1000) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) + +test('should inject LiveReload snippet but not watch', function(t) { + t.plan(2) + t.timeoutAfter(8000) + + var server + + var entry = path.join(__dirname, 'app.js') + budo(entry, { + dir: __dirname, + port: 8000, + outfile: 'bundle.js', + 'live-script': true + }) + .on('error', function(err) { + t.fail(err) + }) + .on('watch', function(event, file) { + t.fail('received a watch event when we should not have') + }) + .on('connect', function(ev) { + matchesHTML(t, ev.uri) + + //write into file + setTimeout(function() { + fs.writeFile(entry, source) + }, 1000) + + setTimeout(function() { + ev.close() + cleanup() + }, 2000) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) + +function matchesHTML(t, uri) { + request.get({ uri: uri + 'index.html' }, function(err, resp, body) { + if (err) t.fail(err) + t.equal(body, getHTML(), 'matches expected HTML') + }) +} + +function getHTML() { + return '' +} \ No newline at end of file diff --git a/test/test-simple.js b/test/test-simple.js deleted file mode 100644 index df55071..0000000 --- a/test/test-simple.js +++ /dev/null @@ -1,195 +0,0 @@ -var rimraf = require('rimraf') -var fs = require('fs') -var path = require('path') -var spawn = require('win-spawn') -var npmSpawn = require('npm-execspawn') -var kill = require('tree-kill') - -var ndjson = require('ndjson') -var test = require('tape') -var concat = require('concat-stream') -var request = require('request') - -var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') - -//Some other tests needed: -// --live -// --live-plugin - -test('should fail without scripts', function(t) { - t.plan(1) - var proc = spawn(cliPath) - proc.stderr.pipe(concat(function(str) { - t.equal(str.toString().trim(), 'No entry scripts specified!') - })) -}) - -test('should run on available port', function(t) { - t.plan(1) - var proc = spawn(cliPath, ['app.js', '-o', 'bundle.js'], { cwd: __dirname, env: process.env }) - var expected = 'Server running at' - - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - var msg = (data && data.message)||'' - t.ok(msg.indexOf(expected)>=0, 'starts server') - kill(proc.pid) - }) - .on('error', function(err) { - t.fail(err) - kill(proc.pid) - }) -}) - -test('should get a bundle.js', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], - budo: ['app.js'], - }) -}) - -test('entry mapping to bundle2.js', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app', '-v', '-o', 'bundle-expected.js', '-d'], - budo: ['app:bundle2.js'], - to: 'bundle2.js' - }) -}) - -test('should get a bundle.js with --outfile', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], - budo: ['app.js', '-o', 'bundle.js'] - }) -}) - -test('should disable source maps with --no-debug', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], - budo: ['app.js', '-o', 'bundle.js', '--no-debug'] - }) -}) - -test('should get a bundle.js with --dir', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - cwd: cwd, - watchify: ['test/app.js', '-v', '-o', 'test/bundle-expected.js', '-d'], - budo: ['test/app.js', '-o', 'bundle.js', '--dir', 'test'] - }) -}) - -test('should create and destroy tmpdir', function(t) { - t.plan(2) - var proc = spawn(cliPath, ['app.js'], { cwd: __dirname, env: process.env }) - var expected = 'temp directory created at ' - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - if (data.level !== 'debug') - return - - var msg = data && data.message - var idx = msg.indexOf(expected) - - if (idx === -1) { - t.fail('no temp dir created') - kill(proc.pid) - } else { - var path = msg.substring(idx+expected.length).trim() - t.ok(true, 'created tmp dir') - proc.on('exit', cleanup(path)) - kill(proc.pid, 'SIGINT') - } - }) - .on('error', function(err) { - t.fail(err) - kill(proc.pid) - }) - - function cleanup(path) { - return function() { - fs.exists(path, function(exists) { - if (exists) { - t.fail('tmpdir not cleaned up '+path) - rimraf(path, function(err) { - if (err) - console.error(err) - }) - } - else t.ok(true, 'tmpdir cleaned up') - }) - } - } -}) - -function runBundleMatch(t, opt) { - opt = opt||{} - - t.plan(1) - t.timeoutAfter(10000) - var cwd = opt.cwd || __dirname - var foundMsg = false - var bundle = path.join(__dirname, 'bundle.js') - var outputFile = 'bundle-expected.js' - var bundleExpected = path.join(__dirname, outputFile) - - //the expected bundle - var watchifyProc = npmSpawn('watchify '+ opt.watchify.join(' '), { cwd: cwd, env: process.env }) - watchifyProc.stderr.on('data',watchifyDone) - watchifyProc.stdout.on('data',watchifyDone) - - function watchifyDone(msg) { - var suc = msg.toString().indexOf(outputFile) - if (suc === -1) - t.fail('watchify process gave unexpected stdout/stderr message' ) - kill(watchifyProc.pid) - - var expected = fs.readFile(bundleExpected, 'utf8', function(err, data) { - if (err) - t.fail(err) - budoMatches(data) - }) - } - - function budoMatches(source) { - var proc = spawn(cliPath, opt.budo, { cwd: cwd, env: process.env }) - proc.on('exit', cleanup) - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - var msg = (data.message||'').toLowerCase() - if (msg.indexOf('temp directory created') >= 0) - return - - var running = 'server running at ' - var idx = msg.indexOf(running) - if (idx >= 0) { - foundMsg = true - setTimeout(function() { //let bundling finish - var serverUrl = msg.substring(idx+running.length) - request.get({ - uri: serverUrl + (opt.to || 'bundle.js') - }, function(err, resp, data) { - t.equal(data, source, 'bundle matches') - kill(proc.pid) - }) - }, 1000) - } else if (!foundMsg) { - t.fail('no server running message in '+ msg) - kill(proc.pid) - } - }) - } - - function cleanup() { - rimraf(bundleExpected, function(err) { - if (err) console.error(err) - }) - rimraf(bundle, function(err) { - if (err) console.error(err) - }) - } -} From f05bec41bcf8a9e6318357cd80ed5cd69c08482c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 16:11:57 -0400 Subject: [PATCH 029/302] 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c301782..7c60453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "1.3.2", + "version": "2.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 9965a95f795a0ba50901aa288f9571e679a83884 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 16:22:02 -0400 Subject: [PATCH 030/302] bump to latest wtch to fix port bug --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c60453..b6f436d 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "shell-quote": "^1.4.3", "through2": "^0.6.3", "tmp": "0.0.24", - "wtch": "^3.2.0", + "wtch": "^3.2.1", "xtend": "^4.0.0" }, "devDependencies": { From bcee69e3d0af5c2bf79cd031d725c48f22e639d6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 16:22:07 -0400 Subject: [PATCH 031/302] 2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6f436d..fe4e97c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.0.0", + "version": "2.0.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From b81252e7ac0e35ec7887115ffe91ca7023d21d97 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 28 Mar 2015 16:31:51 -0400 Subject: [PATCH 032/302] document watch and reload events --- docs/programmatic-usage.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md index a264fd1..05f4111 100644 --- a/docs/programmatic-usage.md +++ b/docs/programmatic-usage.md @@ -51,6 +51,14 @@ Called once the budo server connects. The callback is passed an `event` object t It's recommended to use `glob` instead of `from` if you intend to watch the `bundle.js` for file changes (e.g. determining when the bundle is ready to launch the browser). This is due to some issues with OSX temp dir file watching. +#### `b.on('watch')` + +If `live` or `live-plugin` options were passed, this event will be triggered after a file change or addition is made (such as bundle.js or themes.css). The parameters will be `(eventType, file)` where `eventType` is typically "add" or "change". + +#### `b.on('reload') + +If `live` or `live-plugin` options were passed, this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. + # gulp & grunt Budo works without gulp and grunt, but you may want to wrap it within the same build environment for consistency. From 9f6190beb79e63a85cf117627f7b1f3c6ef53644 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 29 Mar 2015 15:19:46 -0400 Subject: [PATCH 033/302] source cleanup, expose watch() and live(), decouple them, remove wtch, support watchify@3.0 fix ignore watch option (+13 squashed commits) Squashed commits: [b512bcf] remove wtch, update chokidar [63ffce8] update docs [f94cc60] fix breaking test with message differences [b25bde0] fix bug witch watch [1eb5105] working on new watchify@3.0 fixes [a3178fd] fix help commands and update host for live reload [5ae0ff9] adding help, better error messaging, and watchify error messages [b379f96] make live() method more like live: true by injecting script tag [3df4103] more work on refactor; cleaner API [46345ed] live reload refactoring [1f5c544] indentation to 2 spaces [fb57af7] refactoring to get rid of wtch [0e987ba] update live --- README.md | 7 +- bin/cmd.js | 27 +++- bin/help.txt | 17 +++ docs/comparisons.md | 2 +- docs/programmatic-usage.md | 91 ++++++------- example/app.js | 6 +- example/theme.css | 2 +- index.js | 137 +++++++++---------- lib/budo.js | 273 ++++++++++++++++++++++++++----------- lib/default-index.js | 20 +-- lib/file-watch.js | 39 ++++++ lib/get-output.js | 50 +++---- lib/server.js | 105 ++++++++------ lib/tinylr.js | 51 +++++++ lib/tmpdir.js | 52 +++---- lib/watchify.js | 39 +++--- package.json | 13 +- test/test-api.js | 96 +++++++++++-- test/test-cli.js | 7 +- test/test-live.js | 35 +++-- 20 files changed, 705 insertions(+), 364 deletions(-) create mode 100644 bin/help.txt create mode 100644 lib/file-watch.js create mode 100644 lib/tinylr.js diff --git a/README.md b/README.md index 9ffb8c8..8957469 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,10 @@ PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/cas - [basic usage](docs/basics.md) - [comparisons](docs/comparisons.md) +- [programmatic usage (Gulp, Grunt)](docs/programmatic-usage.md) - [error reporting](docs/errors.md) - [running tests and examples](docs/tests-and-examples.md) - [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) -- [programmatic usage (Gulp, Grunt)](docs/programmatic-usage.md) - [rapid prototyping with budō and wzrd](http://mattdesl.svbtle.com/rapid-prototyping) ## usage @@ -58,12 +58,13 @@ Usage: budo [entries] [opts] Options: + --help, -h show help message --outfile, -o path to output bundle --port the port to run, default 9966 --host the host, default "localhost" --dir the directory to serve, and the base for --outfile - --live enable LiveReload integration - --live-plugin enable LiveReload but do not inject script tag + --live enable LiveReload integration with a script tag + --live-plugin enable LiveReload for use with a browser plugin --live-port the LiveReload port, default 35729 ``` diff --git a/bin/cmd.js b/bin/cmd.js index fa764f3..b4b4e20 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -1,18 +1,35 @@ #!/usr/bin/env node -//Starts up budo with some logging, -//also listens for live reload events +//Starts budo with stdout +//Handles --help and error messaging +//Uses auto port-finding var args = process.argv.slice(2) var opts = require('minimist')(args) -var portfinder = require('portfinder') +var getport = require('getport') var entries = opts._ opts.stream = process.stdout delete opts._ -portfinder.basePort = opts.port || opts.p || 9966 -portfinder.getPort(function(err, port) { +var showHelp = opts.h || opts.help + +if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { + console.error('ERROR: no entry scripts specified\n use --help for examples') + return +} + +if (showHelp) { + var vers = require('../package.json').version + console.log('budo '+vers, '\n') + var help = require('path').join(__dirname, 'help.txt') + require('fs').createReadStream(help) + .pipe(process.stdout) + return +} + +var basePort = opts.port || opts.p || 9966 +getport(basePort, function(err, port) { if (err) { console.error("Could not find port", err) process.exit(1) diff --git a/bin/help.txt b/bin/help.txt new file mode 100644 index 0000000..e2404fe --- /dev/null +++ b/bin/help.txt @@ -0,0 +1,17 @@ +Usage: + budo index.js [opts] [browserify opts] + +Options: + --help, -h show help message + --outfile, -o path to output bundle + --port the port to run, default 9966 + --host the host, default "localhost" + --dir the directory to serve, and the base for --outfile + --live enable LiveReload integration + --live-plugin enable LiveReload but do not inject script tag + --live-port the LiveReload port, default 35729 + +Examples: + budo index.js --live --dir app/ --outfile bundle.js + budo index.js --verbose --transform brfs + budo index.js:test.js --port 3000 diff --git a/docs/comparisons.md b/docs/comparisons.md index f818374..95df283 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -26,7 +26,7 @@ wzrd index.js:bundle.js | garnish ## wtch -[wtch](https://github.com/mattdesl/wtch) is a small live-reload utility that budō builds on. It watches for JS/CSS/HTML changes and triggers a live-reload event. It can be used to augment wzrd with some watching capabilities. +[wtch](https://github.com/mattdesl/wtch) is a small live-reload utility not related to budo or watchify. It watches for JS/CSS/HTML changes and triggers a live-reload event. It can be used to augment wzrd with some watching capabilities. ```sh #example ... diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md index 05f4111..95ff50b 100644 --- a/docs/programmatic-usage.md +++ b/docs/programmatic-usage.md @@ -43,29 +43,54 @@ Called once the budo server connects. The callback is passed an `event` object t dir: 'app', //the working directory host: 'localhost', //defaults to localhost port: 9966, //the port we're running on - - //also provides a function to close the budo instance - close() } ``` -It's recommended to use `glob` instead of `from` if you intend to watch the `bundle.js` for file changes (e.g. determining when the bundle is ready to launch the browser). This is due to some issues with OSX temp dir file watching. - #### `b.on('watch')` -If `live` or `live-plugin` options were passed, this event will be triggered after a file change or addition is made (such as bundle.js or themes.css). The parameters will be `(eventType, file)` where `eventType` is typically "add" or "change". +If file watching is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after a file change or addition is made (such as bundle.js or themes.css). The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. + +#### `b.on('reload')` + +If live reload is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. + +#### `b.reload(path)` + +If live reload is enabled (i.e. through `live` or `live-plugin`), this will send a LiveReload event to the given path and then trigger the `"reload"` event. -#### `b.on('reload') +#### `b.live([opt])` -If `live` or `live-plugin` options were passed, this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. +If `live` and `live-plugin` were not specified, you can manually enable the LiveReload server with the specified options object: `port` (default 35729) and `host` (default to the `host` argument provided to budo, or `localhost`). You can also specify `plugin: true` if you do not want the LiveReload snippet injected into the HTML. -# gulp & grunt +#### `b.watch([globs, chokidarOpts])` -Budo works without gulp and grunt, but you may want to wrap it within the same build environment for consistency. +If `live` and `live-plugin` were not specified, you can manually enabe [chokidar's](https://github.com/paulmillr/chokidar) file watching with the specified `globs` (array or string) and options. -### simple recipe +`globs` defaults to watching `**/*.{html,css}` and the watchified bundle. `chokidarOpts` defaults to the options passed to the budo constructor. -A simple case of budo within gulp might look like this: +Example of using `live()` and `watch()` together. + +```js +var budo = require('budo') +var path = require('path') +var app = budo('index.js') + +app + //listen to CSS changes + .watch('*.css', { interval: 300, usePolling: true }) + //start LiveReload server + .live() + //handle file events + .on('watch', function(type, file) { + //tell LiveReload to inject some CSS + if (path.extname(file) === '.css') + app.reload(file) + }) +``` + +# build tools + +Budo doesn't need a Grunt or Gulp specific plugin to work, but you may choose to wrap it within your favourite task runner for consistency. A simple case might look like this: ```js var gulp = require('gulp') @@ -74,8 +99,8 @@ var budo = require('budo') //start our local development server gulp.task('dev', function(cb) { budo('index.js') - .on('connect', function(app) { - console.log("Server started at "+app.uri) + .on('connect', function(ev) { + console.log("Server started at "+ev.uri) }) .on('exit', cb) }) @@ -83,38 +108,8 @@ gulp.task('dev', function(cb) { Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and write `bundle.js` to a temp directory. -### advanced recipes - -The following script shows how you can include a few more features to the task: - -- uses LiveReload on bundle change -- uses `babelify` for ES6 transpiling -- uses `errorify` to display syntax errors in the browser -- pretty-prints server requests to stdout with [garnish](https://github.com/mattdesl/garnish) - -```js -var gulp = require('gulp') -var budo = require('budo') -var garnish = require('garnish') - -//advanced example -gulp.task('default', function(cb) { - //using garnish for pretty-printing server requests - var pretty = garnish() - pretty.pipe(process.stdout) - - budo('index.js', { - live: true, //live reload - stream: pretty, //output stream - port: 8000, //the port to serve on - plugin: 'errorify', //nicer errors during dev - transform: 'babelify' //ES6 transpiling - }).on('exit', cb) -}) -``` - -Note that `babelify` and `errorify` need to be saved as local devDependencies. - -### demo +#### integrations -[budo-gulp-starter](https://github.com/mattdesl/budo-gulp-starter) demonstrates some more complex applications of budo and gulp. \ No newline at end of file +- [gulp](https://github.com/mattdesl/budo-gulp-starter) +- [npm scripts](https://gist.github.com/mattdesl/b6990e7c7221c9cc05aa) +- [LiveReactLoad](https://gist.github.com/mattdesl/2aa5b45ed1f230635a04) \ No newline at end of file diff --git a/example/app.js b/example/app.js index ee518e8..6b84aa3 100644 --- a/example/app.js +++ b/example/app.js @@ -14,15 +14,15 @@ img.src = 'baboon.png' var time = 0 require('raf-loop')(function(dt) { - var width = window.innerWidth, - height = window.innerHeight + var width = ctx.canvas.width, + height = ctx.canvas.height ctx.clearRect(0, 0, width, height) time += dt/1000 ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 200, 35, 150, 150) + ctx.fillRect(Math.sin(time)*50 + 300, 200, 150, 50) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) diff --git a/example/theme.css b/example/theme.css index c0f1b2d..a0e303f 100644 --- a/example/theme.css +++ b/example/theme.css @@ -1,3 +1,3 @@ body { - background: #eee; + background: #efefef; } \ No newline at end of file diff --git a/index.js b/index.js index fe1e8c3..99858ac 100644 --- a/index.js +++ b/index.js @@ -4,87 +4,86 @@ var xtend = require('xtend') var assign = require('xtend/mutable') var Emitter = require('events/') var getOutput = require('./lib/get-output') -var wtch = require('wtch') - -var defaultGlob = '**/*.{html,css}' var budo = require('./lib/budo') module.exports = function(entry, opts) { - var argv = assign({}, opts) + var argv = assign({}, opts) - if (argv.stream) { - bole.output({ - stream: argv.stream, - level: 'debug' - }) - } + if (argv.stream) { + bole.output({ + stream: argv.stream, + level: 'debug' + }) + } + + var emitter = budo() + emitter.on('connect', setupLive) - var emitter = new Emitter() - var watcher + var entries = Array.isArray(entry) ? entry : [entry] + entries = entries.filter(Boolean) + if (entries.length === 0) { + bail("No entry scripts specified!") + return emitter + } - var entries = Array.isArray(entry) ? entry : [ entry ] - entries = entries.filter(Boolean) - if (entries.length === 0) { - bail("No entry scripts specified!") - return emitter - } + argv.port = typeof argv.port === 'number' ? argv.port : 9966 + argv.dir = argv.dir || process.cwd() + var outOpts = xtend(argv, { + __to: entryMapping() + }) - argv.port = typeof argv.port === 'number' ? argv.port : 9966 - argv.dir = argv.dir || process.cwd() - var outOpts = xtend(argv, { __to: entryMapping() }) + getOutput(outOpts, function(err, output) { + if (err) { + bail("Error: Could not create temp bundle.js directory") + return emitter + } - getOutput(outOpts, function(err, output) { - if (err) { - bail("Error: Could not create temp bundle.js directory") - return emitter - } + //run watchify server + emitter._start(entries, output, argv) + .on('error', function(err2) { + var msg = "Error running budo on " + argv.port + ': ' + err2 + bail(msg) + }) + .on('exit', function() { + log.info('closing') + }) + }) - //run watchify server - var app = budo(entries, output, argv) - .on('error', function(err2) { - var msg = "Error running budo on " + argv.port + ': ' + err2 - bail(msg) - }) - .on('exit', function() { - log.info('closing') - emitter.emit('exit') - if (watcher) - watcher.close() - }) - .on('connect', function() { - //setup live reload - if (argv.live || argv['live-plugin']) { - var liveOptions = { - port: argv['live-port'] - } - - //dispatches watch and LiveReload events - watcher = wtch([defaultGlob, app.glob], liveOptions) - watcher.on('watch', emitter.emit.bind(emitter, 'watch')) - watcher.on('reload', emitter.emit.bind(emitter, 'reload')) - } - emitter.emit('connect', app) - }) - }) + return emitter - return emitter - - function entryMapping() { - var to - var first = entries[0] - var parts = first.split(':') - if (parts.length > 1 && parts[1].length > 0) { - var from = parts[0] - to = parts[1] - entries[0] = from - } - return to + //if user requested live: true, set it up with some defaults + function setupLive() { + if (argv.live || argv['live-plugin']) { + emitter + .watch() + .live({ + host: argv.host, + port: argv['live-port'] + }) + .on('watch', function(ev, file) { + if (ev === 'change' || ev === 'add') { + emitter.reload(file) + } + }) } + } - function bail(msg) { - process.nextTick(function() { - emitter.emit('error', new Error(msg)) - }) + function entryMapping() { + var to + var first = entries[0] + var parts = first.split(':') + if (parts.length > 1 && parts[1].length > 0) { + var from = parts[0] + to = parts[1] + entries[0] = from } + return to + } + + function bail(msg) { + process.nextTick(function() { + emitter.emit('error', new Error(msg)) + }) + } } \ No newline at end of file diff --git a/lib/budo.js b/lib/budo.js index 1601fbe..38c3416 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -6,94 +6,213 @@ var assign = require('xtend/mutable') var http = require('./server').http var log = require('bole')('budo') var dargs = require('dargs') +var createFileWatch = require('./file-watch') +var createTinylr = require('./tinylr') -module.exports = function(entries, output, opt) { - opt = opt||{} +module.exports = function() { + var emitter = new Emitter() + var started = false + var closed = false - var port = opt.port || 9966 - var host = opt.host - var serverOpt = xtend(opt, { output: output }) - - var emitter = new Emitter() - var closed = false + var watchifyProc + var server + var watcher + var tinylr + var defaultGlobs + var defaultLiveOpts + var defaultWatchOpts + var deferreds = [] - //spin up watchify instance - var watchifyArgs = getWatchifyArgs(entries, output, opt) - var watchProc = watchify(watchifyArgs) - - var server = http(serverOpt) - .on('error', function(err) { - emitter.emit('error', err) - }) - .listen(port, host, handler) - - emitter.close = function() { - if (closed) return - closed = true - watchProc.kill() - server.close() - emitter.emit('exit') - } - - //when watchify ends, close the server - watchProc.stderr.on('end', function() { - emitter.close() + emitter._start = function(entries, output, opt) { + opt = opt || {} + var port = opt.port + var serverOpt = xtend(opt, { + output: output }) - return emitter - function handler(err) { + server = http(serverOpt) + .on('error', function(err) { + emitter.emit('error', err) + }) + .listen(port, opt.host, function(err) { if (err) { - emitter.emit('error', new Error("Could not connect to server: " + err)) - return + emitter.emit('error', new Error("Could not connect to server: " + err)) + return } - var hostname = (host||'localhost') - var uri = "http://"+hostname+":"+port+"/" - log.info("Server running at", uri) - - //bug with chokidar@1.0.0-rc3 - //anything in OSX tmp dirs need a wildcard - //to work with fsevents - var glob = output.tmp - ? path.join(output.dir, '**.js') - : output.from - - //add the uri / output to budo instance - assign(emitter, { - uri: uri, - port: port, - host: hostname, - server: server - }, output, { glob: glob }) + + //setup "event" param for callback + assignOptions(output, opt) + + //start watchify process + runWatchify(entries, output, opt) + + started = true + + //if user wanted live() or watch() enabled + deferreds.forEach(function(func) { + func() + }) + deferreds.length = 0 + + //finally, emit callback emitter.emit('connect', emitter) + }) + return emitter + } + + //no-op until live() is enabled + emitter.reload = noop + + //enable file watch capabilities + emitter.watch = function(glob, watchOpt) { + if (!started) { + deferreds.push(emitter.watch.bind(null, glob, watchOpt)) + } + else { + watchOpt = xtend(defaultWatchOpts, watchOpt) + watchOpt = getChokidarOpts(watchOpt) //transform to chokidar + watcher = createFileWatch(glob || defaultGlobs, watchOpt) + watcher.on('watch', emitter.emit.bind(emitter, 'watch')) + emitter.watch = noop + } + return emitter + } + + //enable live-reload capabilities + emitter.live = function(liveOpt) { + if (!started) { + deferreds.push(emitter.live.bind(null, liveOpt)) + } else { + liveOpt = liveOpt||{} + server._live = !liveOpt.plugin + tinylr = createTinylr(xtend(defaultLiveOpts, liveOpt)) + emitter.reload = function(path) { + tinylr.reload(path) + emitter.emit('reload', path) + } + emitter.live = noop + } + return emitter + } + + //close everything + emitter.close = function() { + if (closed) + return + closed = true + if (watchifyProc) + watchifyProc.kill() + if (watcher) + watcher.close() + if (tinylr) + tinylr.close() + if (server) + server.close() + emitter.live = noop + emitter.watch = noop + emitter.emit('exit') + } + + return emitter + + function runWatchify(entries, output, opt) { + if (closed) + return + + //setup watchify; when it ends, close the server + var watchifyArgs = getWatchifyArgs(entries, output, opt) + watchifyProc = watchify(watchifyArgs) + watchifyProc.stderr.on('end', function() { + emitter.close() + }) + } + + function assignOptions(output, opt) { + var hostname = (opt.host || 'localhost') + var uri = "http://" + hostname + ":" + opt.port + "/" + log.info("Server running at", uri) + + //bug with chokidar@1.0.0-rc3 + //anything in OSX tmp dirs need a wildcard + //to work with fsevents + var glob = output.tmp ? path.join(output.dir, '*.js') : output.from + + //add the uri / output to budo instance + assign(emitter, { + uri: uri, + port: opt.port, + host: hostname, + server: server, + 'live-port': opt['live-port'] + }, output, { + glob: glob + }) + + //defaults for live() / watch() functions + defaultGlobs = ['**/*.{html,css}', emitter.glob] + //watchify@3 has some extra options + defaultWatchOpts = { + poll: opt.poll, + 'ignore-watch': typeof opt['ignore-watch'] === 'undefined' ? opt.iw : opt['ignore-watch'] + } + defaultLiveOpts = { + plugin: opt['live-plugin'], + host: opt.host, + port: emitter['live-port'] } + } + + function noop() { + return emitter + } +} + +function getChokidarOpts(opt) { + //do not mutate original opts + opt = assign({}, opt) + + if (opt.poll || opt.poll === 0) { + var interval = opt.poll + opt.usePolling = true + opt.interval = typeof interval === 'number' ? interval : 100 + delete opt.poll + } + if (opt['ignore-watch'] || typeof opt['ignore-watch'] === 'string') { + opt.ignored = opt['ignore-watch'] + delete opt['ignore-watch'] + } else { + //otherwise let file-watch ignore some defaults + delete opt.ignored + } + return opt } function getWatchifyArgs(entries, output, opt) { - //do not mutate original opts - opt = assign({}, opt) - - //set output file - opt.outfile = output.from - delete opt.o - - //enable debug by default - if (opt.d !== false && opt.debug !== false) { - delete opt.d - opt.debug = true - } - //if user explicitly disabled debug... - else if (opt.d === false || opt.debug === false) { - delete opt.d - delete opt.debug - } + //do not mutate original opts + opt = assign({}, opt) + + //set output file + opt.outfile = output.from + delete opt.o + + //enable debug by default + if (opt.d !== false && opt.debug !== false) { + delete opt.d + opt.debug = true + } + //if user explicitly disabled debug... + else if (opt.d === false || opt.debug === false) { + delete opt.d + delete opt.debug + } - //clean up some possible collisions - delete opt.dir - delete opt.port - delete opt.host - delete opt.live - delete opt['live-port'] - delete opt['live-script'] - delete opt['live-plugin'] - return entries.concat(dargs(opt)) + //clean up some possible collisions + delete opt.dir + delete opt.port + delete opt.host + delete opt.live + delete opt['live-port'] + delete opt['live-script'] + delete opt['live-plugin'] + return entries.concat(dargs(opt)) } \ No newline at end of file diff --git a/lib/default-index.js b/lib/default-index.js index 2e1cbdb..3bdbd21 100644 --- a/lib/default-index.js +++ b/lib/default-index.js @@ -1,14 +1,14 @@ var through2 = require('through2') module.exports = function(opt) { - var out = through2() - out.end([ - '', - '', - '', - '', - '', - '' - ].join('')) - return out + var out = through2() + out.end([ + '', + '', + '', + '', + '', + '' + ].join('')) + return out } \ No newline at end of file diff --git a/lib/file-watch.js b/lib/file-watch.js new file mode 100644 index 0000000..d34e464 --- /dev/null +++ b/lib/file-watch.js @@ -0,0 +1,39 @@ +//a thin wrapper around chokidar file watching +var log = require('bole')('budo') +var watch = require('chokidar').watch +var xtend = require('xtend') +var Emitter = require('events/') + +var ignores = [ + 'node_modules/**', 'bower_components/**', + '.git', '.hg', '.svn', '.DS_Store', + '*.swp', 'thumbs.db', 'desktop.ini' +] + +module.exports = function(glob, opt) { + opt = xtend({ + ignored: ignores, + ignoreInitial: true + }, opt) + + var emitter = new Emitter() + + watcher = watch(glob, opt) + watcher.on('add', reload.bind(null, 'add')) + watcher.on('change', reload.bind(null, 'change')) + + function reload(event, path) { + emitter.emit('watch', event, path) + log.debug({ + type: event, + url: path + }) + } + + emitter.close = function() { + watcher.close() + } + return emitter +} + +module.exports.ignores = ignores \ No newline at end of file diff --git a/lib/get-output.js b/lib/get-output.js index 92030a3..1e7c37e 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -3,30 +3,30 @@ var tmpdir = require('./tmpdir') //get an output directory, from user or tmp dir module.exports = function getOutput(argv, cb) { - var outfile = argv.o || argv.outfile - if (!outfile) { - //user can specify a mapping for temp dirs - var bundleTo = argv.__to || 'bundle.js' + var outfile = argv.o || argv.outfile + if (!outfile) { + //user can specify a mapping for temp dirs + var bundleTo = argv.__to || 'bundle.js' - tmpdir(function(err, filedir) { - var output - if (!err) { - var file = path.join(filedir, bundleTo) - output = { - tmp: true, - from: file, - to: bundleTo, - dir: filedir - } - } - cb(err, output) - }) - } else { - var from = path.join(argv.dir, outfile) - cb(null, { - from: from, - to: outfile, - dir: argv.dir - }) - } + tmpdir(function(err, filedir) { + var output + if (!err) { + var file = path.join(filedir, bundleTo) + output = { + tmp: true, + from: file, + to: bundleTo, + dir: filedir + } + } + cb(err, output) + }) + } else { + var from = path.join(argv.dir, outfile) + cb(null, { + from: from, + to: outfile, + dir: argv.dir + }) + } } \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 7bd9c1b..76122bd 100644 --- a/lib/server.js +++ b/lib/server.js @@ -9,58 +9,83 @@ var html = require('./default-index') var inject = require('inject-lr-script') module.exports.http = function(opts) { - var handler = module.exports.static(opts) - return http.createServer(handler) + var handler = module.exports.static(opts) + var server = http.createServer(handler) + + Object.defineProperty(server, '_live', { + get: function() { + return handler._live + }, + set: function(live) { + handler._live = live + } + }) + + return server } module.exports.static = function(opts) { - var basedir = opts.dir || process.cwd() - var staticHandler = ecstatic(basedir) - var router = Router() + var basedir = opts.dir || process.cwd() + var staticHandler = ecstatic(basedir) + var router = Router() - var live = opts.live || opts['live-script'] - var liveOpts = { - host: opts.host, - port: opts['live-port'] - } + router._live = opts.live + var liveOpts = { + host: opts.host, + port: opts['live-port'] + } - var out = opts.output - var entryHandler = staticHandler - if (out.tmp) - entryHandler = ecstatic({ root: out.dir }) + var out = opts.output + var entryHandler = staticHandler + if (out.tmp) { + entryHandler = ecstatic({ + root: out.dir + }) + } - router.addRoute('/' + out.to, function(req, res, params) { - log.info({ url: req.url, type: 'static' }) - entryHandler(req, res) + router.addRoute('/' + out.to, function(req, res, params) { + log.info({ + url: req.url, + type: 'static' }) + entryHandler(req, res) + }) - router.addRoute('/index.html', home) - router.addRoute('/', home) + router.addRoute('/index.html', home) + router.addRoute('/', home) - router.addRoute('*', function(req, res, params) { - log.info({ url: req.url, type: 'static' }) - staticHandler(req, res) + router.addRoute('*', function(req, res, params) { + log.info({ + url: req.url, + type: 'static' }) + staticHandler(req, res) + }) - return router + return router - function home(req, res, params) { - fs.exists(path.join(basedir, 'index.html'), function(exists) { - //inject LiveReload into HTML content if needed - if (live) - res = inject(res, liveOpts) - var type = exists ? 'static' : 'generated' - log.info({ url: req.url, type: type }) + function home(req, res, params) { + fs.exists(path.join(basedir, 'index.html'), function(exists) { + //inject LiveReload into HTML content if needed + if (router._live) + res = inject(res, liveOpts) + var type = exists ? 'static' : 'generated' + log.info({ + url: req.url, + type: type + }) - if (exists) - staticHandler(req, res) - else - generateIndex(out.to, req, res) - }) - } + if (exists) + staticHandler(req, res) + else + generateIndex(out.to, req, res) + }) + } - function generateIndex(outfile, req, res) { - res.setHeader('content-type', 'text/html') - html({ outfile: outfile }).pipe(res) - } + function generateIndex(outfile, req, res) { + res.setHeader('content-type', 'text/html') + html({ + outfile: outfile + }).pipe(res) + } } \ No newline at end of file diff --git a/lib/tinylr.js b/lib/tinylr.js new file mode 100644 index 0000000..5956be4 --- /dev/null +++ b/lib/tinylr.js @@ -0,0 +1,51 @@ +//a thin wrapper around tiny-lr module +var log = require('bole')('budo') +var xtend = require('xtend') +var tinylr = require('tiny-lr') + +module.exports = function(glob, opt) { + opt = xtend(opt) + opt.host = opt.host || 'localhost' + if (typeof opt.port !== 'number') + opt.port = 35729 + + var server = tinylr() + var closed = false + + server.listen(opt.port, opt.host, function(a) { + if (closed) + return + log.info('livereload running on ' + opt.port) + }) + + var serverImpl = server.server + serverImpl.removeAllListeners('error') + serverImpl.on('error', function(err) { + if (err.code === 'EADDRINUSE') { + process.stderr.write('ERROR: livereload not started, port ' + opt.port + ' is in use\n') + server.close() + closed = true + } + }) + + return { + close: function close() { + if (closed) + return + server.close() + closed = true + }, + + reload: function reload(path) { + try { + server.changed({ + body: { + files: [path] + } + }) + } catch (e) { + throw e + } + } + } +} \ No newline at end of file diff --git a/lib/tmpdir.js b/lib/tmpdir.js index ca2aeb8..46c3544 100644 --- a/lib/tmpdir.js +++ b/lib/tmpdir.js @@ -5,35 +5,35 @@ var tmp = require('tmp') tmp.setGracefulCleanup() module.exports = function(cb) { - tmp.dir({ - mode: '0755', - prefix: 'budo-' - }, - function(err, filepath) { - if (!err) { - process.on('exit', remove) - process.on('SIGINT', exit) - process.on('uncaughtException', exit) - log.debug('temp directory created at', filepath) - } + tmp.dir({ + mode: '0755', + prefix: 'budo-' + }, + function(err, filepath) { + if (!err) { + process.on('exit', remove) + process.on('SIGINT', exit) + process.on('uncaughtException', exit) + log.debug('temp directory created at', filepath) + } - cb(err, filepath) + cb(err, filepath) - function remove(err) { - try { - rimraf.sync(filepath) - } catch(e) { - rimraf.sync(filepath) - } - if (err) - console.error(err.stack) - } - - function exit(err) { - if (err) - console.error(err.stack) - process.exit() + function remove(err) { + try { + rimraf.sync(filepath) + } catch (e) { + rimraf.sync(filepath) } + if (err) + console.error(err.stack) + } + + function exit(err) { + if (err) + console.error(err.stack) + process.exit() + } }) } \ No newline at end of file diff --git a/lib/watchify.js b/lib/watchify.js index de47480..274c109 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -3,24 +3,29 @@ var quote = require('shell-quote').quote //Runs watchify with given args, returns process module.exports = function(watchifyArgs) { - var cmd = ['watchify'] - .concat(quote(watchifyArgs||[])) - .join(' ') + var cmd = ['watchify'] + .concat(quote(watchifyArgs || [])) + .join(' ') - var proc = spawn(cmd) - proc.stderr.on('data', function(err) { - process.stderr.write(err.toString()) - }) + var proc = spawn(cmd) + proc.stderr.on('data', function(err) { + //nicer messaging for common error cases + if (err.toString().indexOf('watchify: command not found') >= 0) { + process.stderr.write("ERROR: Could not find watchify\n") + process.stderr.write("Make sure to install it locally with --save-dev\n") + } else + process.stderr.write(err.toString()) + }) - var hasClosed = false - process.on('close', handleClose) - process.on('exit', handleClose) - - return proc + var hasClosed = false + process.on('close', handleClose) + process.on('exit', handleClose) - function handleClose() { - if (hasClosed) return - hasClosed = true - proc.kill() - } + return proc + + function handleClose() { + if (hasClosed) return + hasClosed = true + proc.kill() + } } \ No newline at end of file diff --git a/package.json b/package.json index fe4e97c..d693b25 100644 --- a/package.json +++ b/package.json @@ -14,20 +14,21 @@ }, "dependencies": { "bole": "^2.0.0", + "chokidar": "^1.0.0-rc5", "dargs": "^4.0.0", "ecstatic": "^0.5.8", "events": "^1.0.2", + "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "minimist": "^1.1.0", "npm-execspawn": "^1.0.6", - "portfinder": "^0.4.0", "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", "shell-quote": "^1.4.3", "through2": "^0.6.3", + "tiny-lr": "^0.1.5", "tmp": "0.0.24", - "wtch": "^3.2.1", "xtend": "^4.0.0" }, "devDependencies": { @@ -47,15 +48,15 @@ "tape": "^3.5.0", "through2": "^0.6.3", "tree-kill": "0.0.6", - "watchify": "^2.6.2", + "watchify": "^3.0.0", "win-spawn": "^2.0.0" }, "scripts": { "test": "tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", - "live": "./bin/cmd.js example/app.js --dir example --live | garnish -v", - "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish -v", - "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish -v" + "live": "./bin/cmd.js example/app.js --dir example --live | garnish", + "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", + "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish" }, "keywords": [ "browserify", diff --git a/test/test-api.js b/test/test-api.js index 422ece6..72b756c 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -3,8 +3,9 @@ var budo = require('../') var cleanup = require('./cleanup') var path = require('path') -test('should provide an API', function(t) { - t.plan(8) +test('sets watch() after connect', function(t) { + t.plan(9) + t.timeoutAfter(10000) budo('test/app.js', { dir: __dirname, @@ -14,19 +15,86 @@ test('should provide an API', function(t) { .on('error', function(err) { t.fail(err) }) - .on('connect', function(ev) { + .on('connect', function(app) { var file = path.join(__dirname, 'bundle.js') - t.equal(ev.to, 'bundle.js', 'mapping matches') - t.equal(ev.from, file, 'from matches') - t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') - t.equal(ev.host, 'localhost', 'host is not specified') - t.equal(ev.port, 8000, 'port matches') - t.equal(ev.glob, file, 'glob matches file') - t.equal(ev.dir, __dirname, 'dir matches') - setTimeout(function() { - ev.close() - cleanup() - }, 1000) + t.equal(app.to, 'bundle.js', 'mapping matches') + t.equal(app.from, file, 'from matches') + t.equal(app.uri, 'http://localhost:8000/', 'uri matches') + t.equal(app.host, 'localhost', 'host is not specified') + t.equal(app.port, 8000, 'port matches') + t.equal(app.glob, file, 'glob matches file') + t.equal(app.dir, __dirname, 'dir matches') + + app + .watch() + .once('watch', function(type) { + t.ok(true, 'got watch') + app.close() + cleanup() + }) + }) + .on('reload', function() { + t.fail('should not have received reload event') + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) + +test('sets watch() with args before connect', function(t) { + t.plan(3) + t.timeoutAfter(10000) + + var app = budo('test/app.js', { + dir: __dirname, + port: 8000, + outfile: 'bundle.js' + }) + .watch() //enable watcher + .once('watch', function() { + t.ok(true, 'got watch') + app.close() + cleanup() + }) + .on('error', function(err) { + t.fail(err) + }) + .on('reload', function() { + t.fail('should not have received reload event') + }) + .on('connect', function(app) { + var file = path.join(__dirname, 'bundle.js') + t.equal(app.from, file, 'from matches') + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) + +test('sets watch() and live() by default with live: true', function(t) { + t.plan(4) + t.timeoutAfter(10000) + + var app = budo('test/app.js', { + dir: __dirname, + port: 8000, + live: true, + outfile: 'bundle.js' + }) + .once('reload', function(err) { + t.ok(true, 'got reload event') + app.close() + cleanup() + }) + .once('watch', function() { + t.ok(true, 'got watch event') + }) + .on('error', function(err) { + t.fail(err) + }) + .on('connect', function(app) { + var file = path.join(__dirname, 'bundle.js') + t.equal(app.from, file, 'from matches') }) .on('exit', function() { t.ok(true, 'closing') diff --git a/test/test-cli.js b/test/test-cli.js index ea7a8e8..16636af 100644 --- a/test/test-cli.js +++ b/test/test-cli.js @@ -12,15 +12,12 @@ var request = require('request') var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') -// Some other tests needed: -// --live -// --live-plugin - test('should fail without scripts', function(t) { t.plan(1) var proc = spawn(cliPath) proc.stderr.pipe(concat(function(str) { - t.equal(str.toString().trim(), 'No entry scripts specified!') + var msg = str.toString().toLowerCase() + t.notEqual(msg.indexOf('no entry scripts specified'), -1) })) }) diff --git a/test/test-live.js b/test/test-live.js index a437ad7..4b91a16 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -7,7 +7,7 @@ var fs = require('fs') var source = fs.readFileSync(path.join(__dirname, 'app.js'), 'utf8') test('should inject LiveReload snippet', function(t) { - t.plan(3) + t.plan(4) t.timeoutAfter(10000) var server @@ -24,8 +24,11 @@ test('should inject LiveReload snippet', function(t) { }) .on('watch', function(event, file) { t.equal(path.basename(file), 'bundle.js', 'watch event triggered') - server.close() + }) + .on('reload', function(file) { + t.equal(path.basename(file), 'bundle.js', 'reload event triggered') cleanup() + server.close() }) .on('connect', function(ev) { server = ev @@ -39,37 +42,41 @@ test('should inject LiveReload snippet', function(t) { }) }) -test('should inject LiveReload snippet but not watch', function(t) { - t.plan(2) - t.timeoutAfter(8000) + +test('manual LiveReload triggering', function(t) { + t.plan(4) + t.timeoutAfter(10000) var server var entry = path.join(__dirname, 'app.js') - budo(entry, { + var app = budo(entry, { dir: __dirname, port: 8000, outfile: 'bundle.js', + live: false, 'live-script': true }) + .watch() + .live() .on('error', function(err) { t.fail(err) }) .on('watch', function(event, file) { - t.fail('received a watch event when we should not have') + t.equal(path.basename(file), 'bundle.js', 'watch event triggered') + app.reload(file) + }) + .on('reload', function(file) { + t.equal(path.basename(file), 'bundle.js', 'reload event triggered') + cleanup() + server.close() }) .on('connect', function(ev) { + server = ev matchesHTML(t, ev.uri) - - //write into file setTimeout(function() { fs.writeFile(entry, source) }, 1000) - - setTimeout(function() { - ev.close() - cleanup() - }, 2000) }) .on('exit', function() { t.ok(true, 'closing') From 38f0cfe6c88ea15ecabd9f2f249d6799fc5764eb Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 30 Mar 2015 12:44:06 -0400 Subject: [PATCH 034/302] 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d693b25..ee3fe28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.0.1", + "version": "2.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 7171709e8974cf7783413f235ab23d7dceb999e9 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 31 Mar 2015 10:33:59 -0400 Subject: [PATCH 035/302] test with latest watchify, clean up readme --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8957469..600f652 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # budō -[![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mattdesl/budo) This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but with a stronger focus on incremental bundling, LiveReload (including CSS injection), and other [experimental features](#script-injection) down the road. diff --git a/package.json b/package.json index ee3fe28..f160ad6 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "tape": "^3.5.0", "through2": "^0.6.3", "tree-kill": "0.0.6", - "watchify": "^3.0.0", + "watchify": "^3.1.0", "win-spawn": "^2.0.0" }, "scripts": { From 450056d409fbe967fe4665c2852763997c1c7c3e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 31 Mar 2015 11:33:08 -0400 Subject: [PATCH 036/302] supporting watchify@3.1.0 outpipe --- index.js | 5 ++++- lib/budo.js | 2 +- lib/get-output.js | 34 ++++++++++++++++++++++++++++++++++ package.json | 2 ++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 99858ac..d764408 100644 --- a/index.js +++ b/index.js @@ -35,7 +35,10 @@ module.exports = function(entry, opts) { getOutput(outOpts, function(err, output) { if (err) { - bail("Error: Could not create temp bundle.js directory") + if (err.name === 'OUTPIPE') + bail("Error: outpipe argument needs to be sent to a file.\nExample:\n budo index.js -o 'uglifyjs > bundle.js'") + else + bail("Error: Could not create temp bundle.js directory") return emitter } diff --git a/lib/budo.js b/lib/budo.js index 38c3416..e469588 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -192,7 +192,7 @@ function getWatchifyArgs(entries, output, opt) { opt = assign({}, opt) //set output file - opt.outfile = output.from + opt.outfile = output.outpipe || output.from delete opt.o //enable debug by default diff --git a/lib/get-output.js b/lib/get-output.js index 1e7c37e..6db34c9 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -1,5 +1,6 @@ var path = require('path') var tmpdir = require('./tmpdir') +var quote = require('shell-quote') //get an output directory, from user or tmp dir module.exports = function getOutput(argv, cb) { @@ -22,11 +23,44 @@ module.exports = function getOutput(argv, cb) { cb(err, output) }) } else { + var outpipe = null var from = path.join(argv.dir, outfile) + + //support outpipe in watchify@3.1.0 + if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { + var parts = quote.parse(outfile) + var op = indexOfOp(parts) + if (op === -1 || op === parts.length-1) { + var err = new Error("outpipe not supported") + err.name = 'OUTPIPE' + return cb(err) + } + from = path.join(argv.dir, parts[op+1].trim()) + + parts = parts.map(function(part, i) { + if (part.op) + return part.op + if (i === op+1) + return from + return part + }) + outpipe = quote.quote(parts) + } + cb(null, { from: from, to: outfile, + outpipe: outpipe, dir: argv.dir }) } +} + +function indexOfOp(parts) { + var i = 0 + for (; i < parts.length; i++) { + if (parts[i].op) + break + } + return i === parts.length ? -1 : i } \ No newline at end of file diff --git a/package.json b/package.json index f160ad6..8aa599d 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "tape": "^3.5.0", "through2": "^0.6.3", "tree-kill": "0.0.6", + "uglify-js": "^2.4.19", "watchify": "^3.1.0", "win-spawn": "^2.0.0" }, @@ -55,6 +56,7 @@ "test": "tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish", + "uglify": "./bin/cmd.js example/app.js --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish" }, From 9b9c7b245dbcb5eb18fe5288bf124f0882b1f1a0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 31 Mar 2015 11:41:09 -0400 Subject: [PATCH 037/302] cleanup errors and outpipe; add note on caching --- docs/basics.md | 6 ++++++ lib/get-output.js | 6 ++++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/basics.md b/docs/basics.md index 116e037..6189af3 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -90,3 +90,9 @@ budo index.js --outfile bundle.js --live | garnish Now when you save the `index.js` file, it will trigger a live-reload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). + +## caching + +For the best experience, make sure that browser caching is disabled during development. Otherwise you might accidentally get served the empty source file mid-bundle, and subsequent reloads will do nothing. In Chrome you can disable it by hitting the Gears button in the DevTools, and checking `Disable Cache (while DevTools is open)` + +![cache](http://i.imgur.com/e1LpJJi.png) diff --git a/lib/get-output.js b/lib/get-output.js index 6db34c9..8150bab 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -24,14 +24,14 @@ module.exports = function getOutput(argv, cb) { }) } else { var outpipe = null - var from = path.join(argv.dir, outfile) + var from //support outpipe in watchify@3.1.0 if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { var parts = quote.parse(outfile) var op = indexOfOp(parts) if (op === -1 || op === parts.length-1) { - var err = new Error("outpipe not supported") + var err = new Error("unsupported outpipe command") err.name = 'OUTPIPE' return cb(err) } @@ -45,6 +45,8 @@ module.exports = function getOutput(argv, cb) { return part }) outpipe = quote.quote(parts) + } else { + from = path.join(argv.dir, outfile) } cb(null, { diff --git a/package.json b/package.json index 8aa599d..f6fe77a 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "test": "tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish", - "uglify": "./bin/cmd.js example/app.js --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", + "uglify": "./bin/cmd.js example/app.js -v --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish" }, From 4ab724ee8ba23707e9275d423bbaee1a6858e91a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 31 Mar 2015 11:41:15 -0400 Subject: [PATCH 038/302] 2.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6fe77a..41b921e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.0", + "version": "2.1.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From b81bf015d2ef3e22b17a428b7370ca7f4fbb9bb8 Mon Sep 17 00:00:00 2001 From: thibauts Date: Wed, 1 Apr 2015 23:49:27 +0200 Subject: [PATCH 039/302] fixed tinylr params --- lib/tinylr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tinylr.js b/lib/tinylr.js index 5956be4..5d77080 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -3,7 +3,7 @@ var log = require('bole')('budo') var xtend = require('xtend') var tinylr = require('tiny-lr') -module.exports = function(glob, opt) { +module.exports = function(opt) { opt = xtend(opt) opt.host = opt.host || 'localhost' if (typeof opt.port !== 'number') From 9d71f717d625e67974889a51af4a9293383c6085 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 1 Apr 2015 18:01:31 -0400 Subject: [PATCH 040/302] 2.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41b921e..26c0d9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.1", + "version": "2.1.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From a4da79759af384c02f862d935bc4a70e76550b5f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 2 Apr 2015 22:45:56 -0400 Subject: [PATCH 041/302] fixing recursive error bug --- index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/index.js b/index.js index d764408..e34ba47 100644 --- a/index.js +++ b/index.js @@ -44,10 +44,6 @@ module.exports = function(entry, opts) { //run watchify server emitter._start(entries, output, argv) - .on('error', function(err2) { - var msg = "Error running budo on " + argv.port + ': ' + err2 - bail(msg) - }) .on('exit', function() { log.info('closing') }) From 72882b52fd1d12c390a3567fad45a9769b501029 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 00:01:20 -0400 Subject: [PATCH 042/302] making tmpdir removal a bit more robust, fixing test for new watchify, shuffling tests a little --- index.js | 17 ++++++++++ test/test-cli.js | 54 +++++--------------------------- test/test-close.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 47 deletions(-) create mode 100644 test/test-close.js diff --git a/index.js b/index.js index e34ba47..5008051 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var xtend = require('xtend') var assign = require('xtend/mutable') var Emitter = require('events/') var getOutput = require('./lib/get-output') +var rimraf = require('rimraf') var budo = require('./lib/budo') @@ -42,10 +43,26 @@ module.exports = function(entry, opts) { return emitter } + var tmp = output.tmp + var tmpFile = output.from + //run watchify server emitter._start(entries, output, argv) + .on('error', function(err) { + //Some more helpful error messaging + if (err.message === 'listen EADDRINUSE') + console.error("Port", argv.port, "is not available\n") + throw err + }) .on('exit', function() { log.info('closing') + if (tmp && tmpFile) { + //last attempt to remove the bundle file + rimraf(tmpFile, function(err) { + if (err) + log.debug('error cleaning up temp file', err) + }) + } }) }) diff --git a/test/test-cli.js b/test/test-cli.js index 16636af..315c0fd 100644 --- a/test/test-cli.js +++ b/test/test-cli.js @@ -82,53 +82,6 @@ test('should get a bundle.js with --dir', function(t) { }) }) -test('should create and destroy tmpdir', function(t) { - t.plan(2) - //TODO: this test would be cleaner if done through API - t.timeoutAfter(6000) - var proc = spawn(cliPath, ['app.js'], { - cwd: __dirname, - env: process.env - }) - var expected = 'temp directory created at ' - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - if (data.level !== 'debug') - return - - var msg = data && data.message - var idx = msg.indexOf(expected) - - if (idx === -1) { - t.fail('no temp dir created') - kill(proc.pid) - } else { - var path = msg.substring(idx + expected.length).trim() - t.ok(true, 'created tmp dir') - proc.on('exit', cleanup(path)) - kill(proc.pid, 'SIGINT') - } - }) - .on('error', function(err) { - t.fail(err) - kill(proc.pid) - }) - - function cleanup(path) { - return function() { - fs.exists(path, function(exists) { - if (exists) { - t.fail('tmpdir not cleaned up ' + path) - rimraf(path, function(err) { - if (err) - console.error(err) - }) - } else t.ok(true, 'tmpdir cleaned up') - }) - } - } -}) - function runBundleMatch(t, opt) { opt = opt || {} @@ -149,6 +102,13 @@ function runBundleMatch(t, opt) { watchifyProc.stdout.on('data', watchifyDone) function watchifyDone(msg) { + setTimeout(function() { + //timeout needed because of watchify@3.1.0 w/ outpipe + readWatchify(msg) + }, 50) + } + + function readWatchify(msg) { var suc = msg.toString().indexOf(outputFile) if (suc === -1) t.fail('watchify process gave unexpected stdout/stderr message') diff --git a/test/test-close.js b/test/test-close.js new file mode 100644 index 0000000..a90500e --- /dev/null +++ b/test/test-close.js @@ -0,0 +1,78 @@ +var test = require('tape') +var budo = require('../') +var spawn = require('win-spawn') +var fs = require('fs') +var path = require('path') +var cleanup = require('./cleanup') +var ndjson = require('ndjson') +var kill = require('tree-kill') + +var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') + +test('should close budo API and delete tmp dirs', function(t) { + t.plan(3) + t.timeoutAfter(5000) + + var tmpFile + + var app = budo('test/app.js', { + dir: __dirname, + port: 8000, + }) + .on('connect', function(ev) { + tmpFile = ev.from + t.ok(true, 'connected') + setTimeout(function() { + app.close() + }, 1000) + }) + .on('exit', function() { + t.ok(true, 'exiting') + doesNotExist(t, tmpFile) + }) + .on('error', function(err) { + t.fail(err) + }) +}) + +test('should close budo CLI and delete tmp dirs', function(t) { + t.plan(2) + var timeout = 2000 + t.timeoutAfter(timeout) + var tmpDir + + var proc = spawn(cliPath, ['app.js'], { + cwd: __dirname, + env: process.env + }) + proc.stdout.pipe(ndjson.parse()) + .on('data', function(data) { + if (!data || !data.message) + return + + var msg = 'temp directory created at' + var idx = data.message.indexOf(msg) + + if (idx === -1) + return + + tmpDir = data.message.substring(idx).trim() + proc.on('exit', function() { + t.ok(true, 'got exit event') + doesNotExist(t, tmpDir) + }) + kill(proc.pid) + }) + proc.stderr.pipe(process.stderr) +}) + +function doesNotExist(t, file) { + setTimeout(function() { + fs.stat(file, function(err) { + if (err) + t.ok(true, 'tmpfile removed') + else + t.fail('tmpfile was not destroyed') + }) + }, 500) +} \ No newline at end of file From 602d45aa44abceb848b475f905fd095d201649b5 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 00:01:33 -0400 Subject: [PATCH 043/302] 2.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 26c0d9f..f257794 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.2", + "version": "2.1.3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From fb8058a8c445caf20a0dfa89685617ba178370e6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 14:41:34 -0400 Subject: [PATCH 044/302] WIP --- lib/budo.js | 12 +++-- test/test-api.js | 126 +++++++++++++++++++++++------------------------ 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index e469588..7053136 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -111,6 +111,7 @@ module.exports = function() { emitter.live = noop emitter.watch = noop emitter.emit('exit') + return emitter } return emitter @@ -121,10 +122,11 @@ module.exports = function() { //setup watchify; when it ends, close the server var watchifyArgs = getWatchifyArgs(entries, output, opt) - watchifyProc = watchify(watchifyArgs) - watchifyProc.stderr.on('end', function() { - emitter.close() - }) + console.log(watchifyArgs) + // watchifyProc = watchify(watchifyArgs) + // watchifyProc.stderr.on('end', function() { + // emitter.close() + // }) } function assignOptions(output, opt) { @@ -214,5 +216,5 @@ function getWatchifyArgs(entries, output, opt) { delete opt['live-port'] delete opt['live-script'] delete opt['live-plugin'] - return entries.concat(dargs(opt)) + return xtend(opt, { entries: entries }) } \ No newline at end of file diff --git a/test/test-api.js b/test/test-api.js index 72b756c..0c1ad4e 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -3,73 +3,73 @@ var budo = require('../') var cleanup = require('./cleanup') var path = require('path') -test('sets watch() after connect', function(t) { - t.plan(9) - t.timeoutAfter(10000) +// test('sets watch() after connect', function(t) { +// t.plan(9) +// t.timeoutAfter(10000) - budo('test/app.js', { - dir: __dirname, - port: 8000, - outfile: 'bundle.js' - }) - .on('error', function(err) { - t.fail(err) - }) - .on('connect', function(app) { - var file = path.join(__dirname, 'bundle.js') - t.equal(app.to, 'bundle.js', 'mapping matches') - t.equal(app.from, file, 'from matches') - t.equal(app.uri, 'http://localhost:8000/', 'uri matches') - t.equal(app.host, 'localhost', 'host is not specified') - t.equal(app.port, 8000, 'port matches') - t.equal(app.glob, file, 'glob matches file') - t.equal(app.dir, __dirname, 'dir matches') +// budo('test/app.js', { +// dir: __dirname, +// port: 8000, +// outfile: 'bundle.js' +// }) +// .on('error', function(err) { +// t.fail(err) +// }) +// .on('connect', function(app) { +// var file = path.join(__dirname, 'bundle.js') +// t.equal(app.to, 'bundle.js', 'mapping matches') +// t.equal(app.from, file, 'from matches') +// t.equal(app.uri, 'http://localhost:8000/', 'uri matches') +// t.equal(app.host, 'localhost', 'host is not specified') +// t.equal(app.port, 8000, 'port matches') +// t.equal(app.glob, file, 'glob matches file') +// t.equal(app.dir, __dirname, 'dir matches') - app - .watch() - .once('watch', function(type) { - t.ok(true, 'got watch') - app.close() - cleanup() - }) - }) - .on('reload', function() { - t.fail('should not have received reload event') - }) - .on('exit', function() { - t.ok(true, 'closing') - }) -}) +// app +// .watch() +// .once('watch', function(type) { +// t.ok(true, 'got watch') +// app.close() +// cleanup() +// }) +// }) +// .on('reload', function() { +// t.fail('should not have received reload event') +// }) +// .on('exit', function() { +// t.ok(true, 'closing') +// }) +// }) -test('sets watch() with args before connect', function(t) { - t.plan(3) - t.timeoutAfter(10000) +// test('sets watch() with args before connect', function(t) { +// t.plan(3) +// t.timeoutAfter(10000) - var app = budo('test/app.js', { - dir: __dirname, - port: 8000, - outfile: 'bundle.js' - }) - .watch() //enable watcher - .once('watch', function() { - t.ok(true, 'got watch') - app.close() - cleanup() - }) - .on('error', function(err) { - t.fail(err) - }) - .on('reload', function() { - t.fail('should not have received reload event') - }) - .on('connect', function(app) { - var file = path.join(__dirname, 'bundle.js') - t.equal(app.from, file, 'from matches') - }) - .on('exit', function() { - t.ok(true, 'closing') - }) -}) +// var app = budo('test/app.js', { +// dir: __dirname, +// port: 8000, +// outfile: 'bundle.js' +// }) +// .watch() //enable watcher +// .once('watch', function() { +// t.ok(true, 'got watch') +// app.close() +// cleanup() +// }) +// .on('error', function(err) { +// t.fail(err) +// }) +// .on('reload', function() { +// t.fail('should not have received reload event') +// }) +// .on('connect', function(app) { +// var file = path.join(__dirname, 'bundle.js') +// t.equal(app.from, file, 'from matches') +// }) +// .on('exit', function() { +// t.ok(true, 'closing') +// }) +// }) test('sets watch() and live() by default with live: true', function(t) { t.plan(4) From ed88223a1e87e9cd1cbdad6f03e463bf00b14f35 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 14:43:31 -0400 Subject: [PATCH 045/302] remove through from dev dep --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index f257794..579b8c9 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "tap-finished": "0.0.1", "tap-spec": "^2.2.1", "tape": "^3.5.0", - "through2": "^0.6.3", "tree-kill": "0.0.6", "uglify-js": "^2.4.19", "watchify": "^3.1.0", From efeb4630145baca1e475dfbbbe4837cb25d7245c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 15:03:16 -0400 Subject: [PATCH 046/302] working on resolves --- lib/budo.js | 13 ++++++------- lib/get-module.js | 28 ++++++++++++++++++++++++++++ lib/watchify.js | 38 +++++++++++++++++++++++++++++++++++++- package.json | 2 ++ 4 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 lib/get-module.js diff --git a/lib/budo.js b/lib/budo.js index 7053136..525438d 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -1,20 +1,19 @@ var path = require('path') var Emitter = require('events/') -var watchify = require('./watchify') var xtend = require('xtend') var assign = require('xtend/mutable') var http = require('./server').http var log = require('bole')('budo') -var dargs = require('dargs') var createFileWatch = require('./file-watch') var createTinylr = require('./tinylr') +var createWatchify = require('./watchify') module.exports = function() { var emitter = new Emitter() var started = false var closed = false - var watchifyProc + var watchify var server var watcher var tinylr @@ -100,8 +99,8 @@ module.exports = function() { if (closed) return closed = true - if (watchifyProc) - watchifyProc.kill() + if (watchify) + watchify.close() if (watcher) watcher.close() if (tinylr) @@ -123,8 +122,8 @@ module.exports = function() { //setup watchify; when it ends, close the server var watchifyArgs = getWatchifyArgs(entries, output, opt) console.log(watchifyArgs) - // watchifyProc = watchify(watchifyArgs) - // watchifyProc.stderr.on('end', function() { + watchify = createWatchify(watchifyArgs) + // watchify.stderr.on('end', function() { // emitter.close() // }) } diff --git a/lib/get-module.js b/lib/get-module.js new file mode 100644 index 0000000..5aaaf8e --- /dev/null +++ b/lib/get-module.js @@ -0,0 +1,28 @@ +//finds a module by name +var find = require('find-global-packages') +var resolve = require('resolve') +var path = require('path') + +//searches local first, then global +module.exports = function(name, opt, cb) { + resolve(name, opt, function (err, result) { + if (err) + find(onGlobals) + else + cb(null, path.dirname(result)) + }) + + function onGlobals(err, dirs) { + if (err) + return cb(new Error('could not find module "'+name+'"')) + + var results = dirs.filter(function(dir) { + return path.basename(dir) === name + }) + + if (results.length === 0) + return cb(new Error('module "'+name+'" is not installed')) + + return cb(null, results[0]) + } +} \ No newline at end of file diff --git a/lib/watchify.js b/lib/watchify.js index 274c109..7a1518e 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,3 +1,39 @@ + +var path = require('path') +var resolve = require('resolve') +var getModule = require('./get-module') + +module.exports = function(opt) { + getModule('watchify', { basedir: opt.dir }, function(err, result) { + if (err) + console.error(err) + else + console.error("SUCCESS", result) + process.exit(1) + }) + // var dir = __dirname + // var watchify = require(dir) + + // resolve('browserify/bin/args.js', { basedir: dir }, onmodule) + + function onmodule(err, browserifyModule) { + if(err) { + console.error("ERROR RESOLVING", err) + } + + console.error("SUCCESS", browserifyModule) + + process.exit(1) + + // var parseArgs = require(browserifyModule) + // var watcher = watchify(parseArgs([entry].concat(flags))) + } +} + + + + +/* var spawn = require('npm-execspawn') var quote = require('shell-quote').quote @@ -28,4 +64,4 @@ module.exports = function(watchifyArgs) { hasClosed = true proc.kill() } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/package.json b/package.json index f257794..65fe074 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,12 @@ "dargs": "^4.0.0", "ecstatic": "^0.5.8", "events": "^1.0.2", + "find-global-packages": "0.0.1", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "minimist": "^1.1.0", "npm-execspawn": "^1.0.6", + "resolve": "^1.1.6", "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", From cd1ee8e5eb01388846d303a66e6ac6b16b95c834 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 16:04:52 -0400 Subject: [PATCH 047/302] using watchify programmatic API for full control --- bin/cmd.js | 2 +- example/app.js | 2 +- lib/budo.js | 40 +++++++++++++------ lib/file-watch.js | 2 +- lib/watchify.js | 100 ++++++++++++++++++++++++++++++++++++---------- package.json | 1 + 6 files changed, 110 insertions(+), 37 deletions(-) diff --git a/bin/cmd.js b/bin/cmd.js index b4b4e20..2835d69 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -31,7 +31,7 @@ if (showHelp) { var basePort = opts.port || opts.p || 9966 getport(basePort, function(err, port) { if (err) { - console.error("Could not find port", err) + console.error("Could not find available port", err) process.exit(1) } opts.port = port diff --git a/example/app.js b/example/app.js index 6b84aa3..41114f8 100644 --- a/example/app.js +++ b/example/app.js @@ -22,7 +22,7 @@ require('raf-loop')(function(dt) { ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 200, 150, 50) + ctx.fillRect(Math.sin(time)*50 + 300, 200, 50, 50) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) diff --git a/lib/budo.js b/lib/budo.js index 525438d..3b6ffaf 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -7,6 +7,7 @@ var log = require('bole')('budo') var createFileWatch = require('./file-watch') var createTinylr = require('./tinylr') var createWatchify = require('./watchify') +var dargs = require('dargs') module.exports = function() { var emitter = new Emitter() @@ -119,13 +120,26 @@ module.exports = function() { if (closed) return - //setup watchify; when it ends, close the server - var watchifyArgs = getWatchifyArgs(entries, output, opt) - console.log(watchifyArgs) - watchify = createWatchify(watchifyArgs) - // watchify.stderr.on('end', function() { - // emitter.close() - // }) + //get watchify args (with --debug by default) + var watchifyArgs = getWatchifyArgs(entries, opt) + + //create a new watchify instance + watchify = createWatchify(watchifyArgs, { + dir: opt.dir, + verbose: opt.v || opt.verbose, + delay: typeof opt.delay === 'number' ? opt.delay : 0, + outfile: output.from + }) + + watchify.on('bundle:pending', function() { + console.log("pending") + }) + watchify.on('bundle:start', function() { + console.log("start") + }) + watchify.on('bundle:end', function() { + console.log("end") + }) } function assignOptions(output, opt) { @@ -138,6 +152,8 @@ module.exports = function() { //to work with fsevents var glob = output.tmp ? path.join(output.dir, '*.js') : output.from + //TODO: get rid of this since it introduces some confusion + // (esp. related to 'serving dir' and 'output dir' which may be different) //add the uri / output to budo instance assign(emitter, { uri: uri, @@ -188,14 +204,10 @@ function getChokidarOpts(opt) { return opt } -function getWatchifyArgs(entries, output, opt) { +function getWatchifyArgs(entries, opt) { //do not mutate original opts opt = assign({}, opt) - //set output file - opt.outfile = output.outpipe || output.from - delete opt.o - //enable debug by default if (opt.d !== false && opt.debug !== false) { delete opt.d @@ -209,11 +221,13 @@ function getWatchifyArgs(entries, output, opt) { //clean up some possible collisions delete opt.dir + delete opt.o + delete opt.outfile delete opt.port delete opt.host delete opt.live delete opt['live-port'] delete opt['live-script'] delete opt['live-plugin'] - return xtend(opt, { entries: entries }) + return entries.concat(dargs(opt)) } \ No newline at end of file diff --git a/lib/file-watch.js b/lib/file-watch.js index d34e464..3aaa995 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -1,4 +1,4 @@ -//a thin wrapper around chokidar file watching +//a thin wrapper around chokidar file watching HTML / CSS var log = require('bole')('budo') var watch = require('chokidar').watch var xtend = require('xtend') diff --git a/lib/watchify.js b/lib/watchify.js index 7a1518e..99ec337 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -2,35 +2,93 @@ var path = require('path') var resolve = require('resolve') var getModule = require('./get-module') +var dargs = require('dargs') +var Emitter = require('events/') +var debounce = require('debounce') +var fs = require('fs') + +module.exports = function(watchifyArgs, opt) { + var emitter = new Emitter() + var delay = opt.delay + var pending = false + var closed = false + var watchify + + emitter.close = function() { + if (closed) + return + closed = true + if (watchify) + watchify.close() + } -module.exports = function(opt) { - getModule('watchify', { basedir: opt.dir }, function(err, result) { + find(opt, function(err, fromArgs) { if (err) - console.error(err) - else - console.error("SUCCESS", result) - process.exit(1) - }) - // var dir = __dirname - // var watchify = require(dir) - - // resolve('browserify/bin/args.js', { basedir: dir }, onmodule) - - function onmodule(err, browserifyModule) { - if(err) { - console.error("ERROR RESOLVING", err) - } + return emitter.emit('error', err) + if (closed) + return + + watchify = fromArgs(watchifyArgs) + + var bytes, time + watchify.on('bytes', function (b) { bytes = b }) + watchify.on('time', function (t) { time = t }) + + var bundleDebounced = debounce(bundle, delay) + watchify.on('update', function() { + pending = true + emitter.emit('bundle:pending') + bundleDebounced() + }) + bundle() + }) + + function bundle() { + if (closed) + return - console.error("SUCCESS", browserifyModule) + pending = false + emitter.emit('bundle:start') - process.exit(1) + var didError = false + var outStream = fs.createWriteStream(opt.outfile) - // var parseArgs = require(browserifyModule) - // var watcher = watchify(parseArgs([entry].concat(flags))) + var wb = watchify.bundle() + wb.on('error', function(err) { + console.error(String(err)) + didError = true + outStream.end('console.error(' + JSON.stringify(String(err)) + ');') + }) + wb.pipe(outStream) + + outStream.on('error', function(err) { + console.error(err) + }) + outStream.on('close', function() { + if (opt.verbose && !didError) { + console.error(bytes + ' bytes written to ' + outfile + ' (' + (time / 1000).toFixed(2) + ' seconds)') + } + emitter.emit('bundle:end') + }) } -} + return emitter +} +function find(opt, cb) { + getModule('watchify', { basedir: opt.dir }, function(err, result) { + if (err) + return cb(err) + + resolve('watchify/bin/args.js', { basedir: result }, resolved) + function resolved(err, watchifyModule) { + if (err) + return cb(err) + var fromArgs = require(watchifyModule) + cb(null, fromArgs) + } + }) +} /* diff --git a/package.json b/package.json index 65fe074..c290919 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "bole": "^2.0.0", "chokidar": "^1.0.0-rc5", "dargs": "^4.0.0", + "debounce": "^1.0.0", "ecstatic": "^0.5.8", "events": "^1.0.2", "find-global-packages": "0.0.1", From 178e499b5bda30aca5160c7edd9081b2ad3fa31e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 2 Apr 2015 22:41:44 -0400 Subject: [PATCH 048/302] revert (+1 squashed commit) Squashed commits: [1d0d6b0] testing out some pending stuff with mid-bundle updates --- lib/server.js | 67 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/lib/server.js b/lib/server.js index 76122bd..4a699bc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -7,20 +7,30 @@ var log = require('bole')('budo') var fs = require('fs') var html = require('./default-index') var inject = require('inject-lr-script') +var Emitter = require('events/') module.exports.http = function(opts) { var handler = module.exports.static(opts) - var server = http.createServer(handler) + var server = http.createServer(handler.router) Object.defineProperty(server, '_live', { get: function() { - return handler._live + return handler.live }, set: function(live) { - handler._live = live + handler.live = live } }) + server.update = function() { + handler.pending = false + handler.emit('update') + } + + server.pending = function() { + handler.pending = true + } + return server } @@ -28,8 +38,13 @@ module.exports.static = function(opts) { var basedir = opts.dir || process.cwd() var staticHandler = ecstatic(basedir) var router = Router() + + var emitter = new Emitter() + var pendingTimeout + emitter.live = opts.live + emitter.router = router + emitter.pending = false - router._live = opts.live var liveOpts = { host: opts.host, port: opts['live-port'] @@ -48,7 +63,31 @@ module.exports.static = function(opts) { url: req.url, type: 'static' }) - entryHandler(req, res) + + if (emitter.pending) { + console.log("Bundle pending...") + var served = false + var ready = function() { + console.log("Late serve") + served = true + setTimeout(function() { + entryHandler(req, res) + }, 100) + } + emitter.removeAllListeners('update') + emitter.once('update', ready) + setTimeout(function() { + if (served) + return + console.log("Reached timeout without being ready") + emitter.removeListener('update', ready) + res.writeHead(404) + res.end('404') + }, 2000) + } else { + console.log("Bundle ready...") + entryHandler(req, res) + } }) router.addRoute('/index.html', home) @@ -62,12 +101,26 @@ module.exports.static = function(opts) { staticHandler(req, res) }) - return router + return emitter + + function attempt(req, res) { + var tries = 3, + interval = 50 + // fs.stat(out.from, function(err, stat) { + // if (err) + // console.error(err) + // else + // console.error(stat.size) + + // }) + // if (stat.size > 0) + entryHandler(req, res) + } function home(req, res, params) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed - if (router._live) + if (emitter.live) res = inject(res, liveOpts) var type = exists ? 'static' : 'generated' log.info({ From b8340fc10818fb2d11a36030f579e0d145f57454 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 17:42:16 -0400 Subject: [PATCH 049/302] more robust way of finding watchify depending on npm config --- bin/cmd.js | 42 ++++++++++++++++-------------- index.js | 8 +----- lib/budo.js | 15 ++++++----- lib/get-module.js | 56 +++++++++++++++++++++++++++++++++++----- lib/get-output.js | 31 +++-------------------- lib/get-watchify.js | 22 ++++++++++++++++ lib/server.js | 59 +++++++++++++----------------------------- lib/watchify.js | 62 ++++++++++++++++++++++----------------------- package.json | 4 ++- 9 files changed, 158 insertions(+), 141 deletions(-) create mode 100644 lib/get-watchify.js diff --git a/bin/cmd.js b/bin/cmd.js index 2835d69..0441e5d 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -15,29 +15,33 @@ delete opts._ var showHelp = opts.h || opts.help if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { - console.error('ERROR: no entry scripts specified\n use --help for examples') - return + console.error('ERROR: no entry scripts specified\n use --help for examples') + return } if (showHelp) { - var vers = require('../package.json').version - console.log('budo '+vers, '\n') - var help = require('path').join(__dirname, 'help.txt') - require('fs').createReadStream(help) - .pipe(process.stdout) - return + var vers = require('../package.json').version + console.log('budo ' + vers, '\n') + var help = require('path').join(__dirname, 'help.txt') + require('fs').createReadStream(help) + .pipe(process.stdout) + return } var basePort = opts.port || opts.p || 9966 getport(basePort, function(err, port) { - if (err) { - console.error("Could not find available port", err) - process.exit(1) - } - opts.port = port - require('../')(entries, opts) - .on('error', function(err2) { - console.error(err2.message) - process.exit(1) - }) -}) + if (err) { + console.error("Could not find available port", err) + process.exit(1) + } + opts.port = port + require('../')(entries, opts) + .on('error', function(err) { + //Some more helpful error messaging + if (err.message === 'listen EADDRINUSE') + console.error("Port", port, "is not available\n") + else + console.error(err) + process.exit(1) + }) +}) \ No newline at end of file diff --git a/index.js b/index.js index 5008051..7f18adb 100644 --- a/index.js +++ b/index.js @@ -48,16 +48,10 @@ module.exports = function(entry, opts) { //run watchify server emitter._start(entries, output, argv) - .on('error', function(err) { - //Some more helpful error messaging - if (err.message === 'listen EADDRINUSE') - console.error("Port", argv.port, "is not available\n") - throw err - }) .on('exit', function() { log.info('closing') if (tmp && tmpFile) { - //last attempt to remove the bundle file + //last attempt to remove the temporary bundle file rimraf(tmpFile, function(err) { if (err) log.debug('error cleaning up temp file', err) diff --git a/lib/budo.js b/lib/budo.js index 3b6ffaf..f13eef5 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -131,14 +131,12 @@ module.exports = function() { outfile: output.from }) - watchify.on('bundle:pending', function() { - console.log("pending") + watchify.on('pending', function() { + if (server) server.pending() }) - watchify.on('bundle:start', function() { - console.log("start") - }) - watchify.on('bundle:end', function() { - console.log("end") + watchify.on('update', function() { + if (server) server.update() + emitter.emit('update', output.from) }) } @@ -208,6 +206,9 @@ function getWatchifyArgs(entries, opt) { //do not mutate original opts opt = assign({}, opt) + //disable delay since we will handle debouncing manually + opt.delay = 0 + //enable debug by default if (opt.d !== false && opt.debug !== false) { delete opt.d diff --git a/lib/get-module.js b/lib/get-module.js index 5aaaf8e..d4c6142 100644 --- a/lib/get-module.js +++ b/lib/get-module.js @@ -2,6 +2,9 @@ var find = require('find-global-packages') var resolve = require('resolve') var path = require('path') +var which = require('npm-which') +var fs = require('fs') +var findParent = require('find-parent-dir') //searches local first, then global module.exports = function(name, opt, cb) { @@ -13,16 +16,55 @@ module.exports = function(name, opt, cb) { }) function onGlobals(err, dirs) { - if (err) - return cb(new Error('could not find module "'+name+'"')) + if (err) //problem finding globals + return npmWhich() var results = dirs.filter(function(dir) { return path.basename(dir) === name }) - if (results.length === 0) - return cb(new Error('module "'+name+'" is not installed')) - - return cb(null, results[0]) + if (results.length === 0) { + return npmWhich() + } else + return cb(null, results[0]) + } + + function npmWhich() { + //last resort, e.g. /Users/username/npm/bin/watchify + which(opt.basedir)(name, function(err, bin) { + if (err) return cb(err) + //assume symlink, get real target + fs.realpath(bin, function(err, link) { + if (err) + return cb(bail()) + + //walk upward until we hit the folder by name + var binPath = getBinPath(link) + if (binPath) + return cb(null, binPath) + return cb(bail()) + }) + }) + } + + function getBinPath(file) { + var found, + last + while (file !== last) { + var base = path.basename(file) + if (base === 'node_modules') + break + if (base === name) { + found = file + break + } + last = file + file = path.resolve(file, '..') + } + return found + } + + function bail() { + return new Error('"'+name+'" is not installed globally or locally') } -} \ No newline at end of file +} diff --git a/lib/get-output.js b/lib/get-output.js index 8150bab..76e3dce 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -26,25 +26,11 @@ module.exports = function getOutput(argv, cb) { var outpipe = null var from - //support outpipe in watchify@3.1.0 + //outpipe was added in watchify@3.1.0 if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { - var parts = quote.parse(outfile) - var op = indexOfOp(parts) - if (op === -1 || op === parts.length-1) { - var err = new Error("unsupported outpipe command") - err.name = 'OUTPIPE' - return cb(err) - } - from = path.join(argv.dir, parts[op+1].trim()) - - parts = parts.map(function(part, i) { - if (part.op) - return part.op - if (i === op+1) - return from - return part - }) - outpipe = quote.quote(parts) + var err = new Error("unsupported outpipe command") + err.name = 'OUTPIPE' + return cb(err) } else { from = path.join(argv.dir, outfile) } @@ -56,13 +42,4 @@ module.exports = function getOutput(argv, cb) { dir: argv.dir }) } -} - -function indexOfOp(parts) { - var i = 0 - for (; i < parts.length; i++) { - if (parts[i].op) - break - } - return i === parts.length ? -1 : i } \ No newline at end of file diff --git a/lib/get-watchify.js b/lib/get-watchify.js new file mode 100644 index 0000000..db12435 --- /dev/null +++ b/lib/get-watchify.js @@ -0,0 +1,22 @@ +var resolve = require('resolve') +var getModule = require('./get-module') + +//finds watchify/bin/args.js local or global +module.exports = function getWatchify(opt, cb) { + if (typeof opt === 'function') { + cb = opt + opt = { basedir: process.cwd() } + } + getModule('watchify', opt, function(err, result) { + if (err) + return cb(err) + + resolve('watchify/bin/args.js', { basedir: result }, resolved) + function resolved(err, watchifyModule) { + if (err) + return cb(err) + var fromArgs = require(watchifyModule) + cb(null, fromArgs) + } + }) +} \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 4a699bc..73b7e45 100644 --- a/lib/server.js +++ b/lib/server.js @@ -8,6 +8,7 @@ var fs = require('fs') var html = require('./default-index') var inject = require('inject-lr-script') var Emitter = require('events/') +var noop = function(){} module.exports.http = function(opts) { var handler = module.exports.static(opts) @@ -40,11 +41,12 @@ module.exports.static = function(opts) { var router = Router() var emitter = new Emitter() - var pendingTimeout emitter.live = opts.live emitter.router = router emitter.pending = false + var previous = noop + var liveOpts = { host: opts.host, port: opts['live-port'] @@ -62,28 +64,15 @@ module.exports.static = function(opts) { log.info({ url: req.url, type: 'static' - }) + }) if (emitter.pending) { console.log("Bundle pending...") - var served = false - var ready = function() { - console.log("Late serve") - served = true - setTimeout(function() { - entryHandler(req, res) - }, 100) - } - emitter.removeAllListeners('update') - emitter.once('update', ready) - setTimeout(function() { - if (served) - return - console.log("Reached timeout without being ready") - emitter.removeListener('update', ready) - res.writeHead(404) - res.end('404') - }, 2000) + // emitter.removeAllListeners('update') + emitter.once('update', function() { + console.log("Bundle updated...") + entryHandler(req, res) + }) } else { console.log("Bundle ready...") entryHandler(req, res) @@ -94,39 +83,25 @@ module.exports.static = function(opts) { router.addRoute('/', home) router.addRoute('*', function(req, res, params) { - log.info({ - url: req.url, - type: 'static' - }) + // log.info({ + // url: req.url, + // type: 'static' + // }) staticHandler(req, res) }) return emitter - function attempt(req, res) { - var tries = 3, - interval = 50 - // fs.stat(out.from, function(err, stat) { - // if (err) - // console.error(err) - // else - // console.error(stat.size) - - // }) - // if (stat.size > 0) - entryHandler(req, res) - } - function home(req, res, params) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed if (emitter.live) res = inject(res, liveOpts) var type = exists ? 'static' : 'generated' - log.info({ - url: req.url, - type: type - }) + // log.info({ + // url: req.url, + // type: type + // }) if (exists) staticHandler(req, res) diff --git a/lib/watchify.js b/lib/watchify.js index 99ec337..17063c8 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,18 +1,19 @@ - +var log = require('bole')('budo') var path = require('path') -var resolve = require('resolve') -var getModule = require('./get-module') +var getWatchify = require('./get-watchify') var dargs = require('dargs') var Emitter = require('events/') var debounce = require('debounce') var fs = require('fs') +var path = require('path') module.exports = function(watchifyArgs, opt) { var emitter = new Emitter() var delay = opt.delay - var pending = false var closed = false + var pending = false var watchify + var time = Date.now() emitter.close = function() { if (closed) @@ -22,7 +23,7 @@ module.exports = function(watchifyArgs, opt) { watchify.close() } - find(opt, function(err, fromArgs) { + getWatchify({ basedir: opt.dir }, function(err, fromArgs) { if (err) return emitter.emit('error', err) if (closed) @@ -30,28 +31,31 @@ module.exports = function(watchifyArgs, opt) { watchify = fromArgs(watchifyArgs) - var bytes, time - watchify.on('bytes', function (b) { bytes = b }) - watchify.on('time', function (t) { time = t }) - var bundleDebounced = debounce(bundle, delay) watchify.on('update', function() { + emitter.emit('pending') pending = true - emitter.emit('bundle:pending') + time = Date.now() bundleDebounced() }) + + //initial bundle + time = Date.now() bundle() }) function bundle() { - if (closed) + if (closed) { + if (pending) { + pending = false + emitter.emit('update') + } return - - pending = false - emitter.emit('bundle:start') + } var didError = false var outStream = fs.createWriteStream(opt.outfile) + var basename = path.basename(opt.outfile) var wb = watchify.bundle() wb.on('error', function(err) { @@ -63,32 +67,28 @@ module.exports = function(watchifyArgs, opt) { outStream.on('error', function(err) { console.error(err) + if (pending) { + pending = false + emitter.emit('update') + } }) outStream.on('close', function() { if (opt.verbose && !didError) { - console.error(bytes + ' bytes written to ' + outfile + ' (' + (time / 1000).toFixed(2) + ' seconds)') + var delay = Date.now() - time + log.info({ + elapsed: (delay / 1000).toFixed(2) + ' s', + type: 'bundle', + url: basename + }) } - emitter.emit('bundle:end') + pending = false + emitter.emit('update') }) } - return emitter } -function find(opt, cb) { - getModule('watchify', { basedir: opt.dir }, function(err, result) { - if (err) - return cb(err) - - resolve('watchify/bin/args.js', { basedir: result }, resolved) - function resolved(err, watchifyModule) { - if (err) - return cb(err) - var fromArgs = require(watchifyModule) - cb(null, fromArgs) - } - }) -} + /* diff --git a/package.json b/package.json index c290919..1782154 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,12 @@ "ecstatic": "^0.5.8", "events": "^1.0.2", "find-global-packages": "0.0.1", + "find-parent-dir": "^0.3.0", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "minimist": "^1.1.0", "npm-execspawn": "^1.0.6", + "npm-which": "^2.0.0", "resolve": "^1.1.6", "response-stream": "0.0.0", "rimraf": "^2.2.8", @@ -57,7 +59,7 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", + "start": "./bin/cmd.js example/app.js -o bundle.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish", "uglify": "./bin/cmd.js example/app.js -v --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", From d87a7adc4ba21936e748b3ee165dac1b4dd4ae9d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 18:21:55 -0400 Subject: [PATCH 050/302] getting things to a working place --- bin/cmd.js | 2 +- example/app.js | 2 +- index.js | 4 ++-- lib/budo.js | 10 ++++---- lib/get-module.js | 17 ++++++++++---- lib/watchify.js | 60 +++++++++++------------------------------------ package.json | 2 +- 7 files changed, 37 insertions(+), 60 deletions(-) diff --git a/bin/cmd.js b/bin/cmd.js index 0441e5d..e06949e 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -41,7 +41,7 @@ getport(basePort, function(err, port) { if (err.message === 'listen EADDRINUSE') console.error("Port", port, "is not available\n") else - console.error(err) + console.error('Error:\n ' + err.message) process.exit(1) }) }) \ No newline at end of file diff --git a/example/app.js b/example/app.js index 41114f8..b9351b5 100644 --- a/example/app.js +++ b/example/app.js @@ -22,7 +22,7 @@ require('raf-loop')(function(dt) { ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 200, 50, 50) + ctx.fillRect(Math.sin(time)*50 + 300, 200, 50, 25) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) diff --git a/index.js b/index.js index 7f18adb..967beb4 100644 --- a/index.js +++ b/index.js @@ -37,9 +37,9 @@ module.exports = function(entry, opts) { getOutput(outOpts, function(err, output) { if (err) { if (err.name === 'OUTPIPE') - bail("Error: outpipe argument needs to be sent to a file.\nExample:\n budo index.js -o 'uglifyjs > bundle.js'") + bail("outpipe with --outfile currently not supported") else - bail("Error: Could not create temp bundle.js directory") + bail("Could not create temp bundle.js directory") return emitter } diff --git a/lib/budo.js b/lib/budo.js index f13eef5..f77bd0d 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -9,6 +9,8 @@ var createTinylr = require('./tinylr') var createWatchify = require('./watchify') var dargs = require('dargs') +var DEFAULT_DELAY = 0 + module.exports = function() { var emitter = new Emitter() var started = false @@ -127,17 +129,17 @@ module.exports = function() { watchify = createWatchify(watchifyArgs, { dir: opt.dir, verbose: opt.v || opt.verbose, - delay: typeof opt.delay === 'number' ? opt.delay : 0, + delay: typeof opt.delay === 'number' ? opt.delay : DEFAULT_DELAY, outfile: output.from }) - - watchify.on('pending', function() { + .on('pending', function() { if (server) server.pending() }) - watchify.on('update', function() { + .on('update', function() { if (server) server.update() emitter.emit('update', output.from) }) + .on('error', emitter.emit.bind(emitter, 'error')) } function assignOptions(output, opt) { diff --git a/lib/get-module.js b/lib/get-module.js index d4c6142..23ab248 100644 --- a/lib/get-module.js +++ b/lib/get-module.js @@ -1,4 +1,6 @@ -//finds a module by name +//finds the directory of a CLI module by name +//searches local first, then global + var find = require('find-global-packages') var resolve = require('resolve') var path = require('path') @@ -6,7 +8,6 @@ var which = require('npm-which') var fs = require('fs') var findParent = require('find-parent-dir') -//searches local first, then global module.exports = function(name, opt, cb) { resolve(name, opt, function (err, result) { if (err) @@ -32,7 +33,8 @@ module.exports = function(name, opt, cb) { function npmWhich() { //last resort, e.g. /Users/username/npm/bin/watchify which(opt.basedir)(name, function(err, bin) { - if (err) return cb(err) + if (err) + return cb(bail()) //assume symlink, get real target fs.realpath(bin, function(err, link) { if (err) @@ -65,6 +67,11 @@ module.exports = function(name, opt, cb) { } function bail() { - return new Error('"'+name+'" is not installed globally or locally') + var msg = [ + '"'+name+'" is not installed globally or locally', + 'Example:', + ' npm install watchify --save-dev\n' + ].join('\n') + return new Error(msg) } -} +} \ No newline at end of file diff --git a/lib/watchify.js b/lib/watchify.js index 17063c8..59c6dc5 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -11,10 +11,12 @@ module.exports = function(watchifyArgs, opt) { var emitter = new Emitter() var delay = opt.delay var closed = false - var pending = false + var pending = true var watchify var time = Date.now() + var first = true + emitter.close = function() { if (closed) return @@ -41,15 +43,14 @@ module.exports = function(watchifyArgs, opt) { //initial bundle time = Date.now() + emitter.emit('pending') + pending = true bundle() }) function bundle() { if (closed) { - if (pending) { - pending = false - emitter.emit('update') - } + update() return } @@ -67,10 +68,7 @@ module.exports = function(watchifyArgs, opt) { outStream.on('error', function(err) { console.error(err) - if (pending) { - pending = false - emitter.emit('update') - } + update() }) outStream.on('close', function() { if (opt.verbose && !didError) { @@ -81,45 +79,15 @@ module.exports = function(watchifyArgs, opt) { url: basename }) } - pending = false - emitter.emit('update') + update() }) } return emitter -} - - - - -/* -var spawn = require('npm-execspawn') -var quote = require('shell-quote').quote -//Runs watchify with given args, returns process -module.exports = function(watchifyArgs) { - var cmd = ['watchify'] - .concat(quote(watchifyArgs || [])) - .join(' ') - - var proc = spawn(cmd) - proc.stderr.on('data', function(err) { - //nicer messaging for common error cases - if (err.toString().indexOf('watchify: command not found') >= 0) { - process.stderr.write("ERROR: Could not find watchify\n") - process.stderr.write("Make sure to install it locally with --save-dev\n") - } else - process.stderr.write(err.toString()) - }) - - var hasClosed = false - process.on('close', handleClose) - process.on('exit', handleClose) - - return proc - - function handleClose() { - if (hasClosed) return - hasClosed = true - proc.kill() + function update() { + if (pending) { + pending = false + emitter.emit('update') + } } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/package.json b/package.json index 1782154..8edc7ce 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "through2": "^0.6.3", "tree-kill": "0.0.6", "uglify-js": "^2.4.19", - "watchify": "^3.1.0", + "watchify": "^3.0.1", "win-spawn": "^2.0.0" }, "scripts": { From e639ebdd0c8514c77aad7ac94d154d77e17c5a08 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 18:42:16 -0400 Subject: [PATCH 051/302] fixing live reload to new workflow --- example/app.js | 2 +- index.js | 3 +++ lib/budo.js | 11 ++--------- lib/watchify.js | 7 ++++--- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/example/app.js b/example/app.js index b9351b5..d699ecc 100644 --- a/example/app.js +++ b/example/app.js @@ -22,7 +22,7 @@ require('raf-loop')(function(dt) { ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 200, 50, 25) + ctx.fillRect(Math.sin(time)*50 + 300, 200, 500, 105) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) diff --git a/index.js b/index.js index 967beb4..14b1821 100644 --- a/index.js +++ b/index.js @@ -76,6 +76,9 @@ module.exports = function(entry, opts) { emitter.reload(file) } }) + .on('update', function(file) { + emitter.reload(file) + }) } } diff --git a/lib/budo.js b/lib/budo.js index f77bd0d..f2f1821 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -147,11 +147,6 @@ module.exports = function() { var uri = "http://" + hostname + ":" + opt.port + "/" log.info("Server running at", uri) - //bug with chokidar@1.0.0-rc3 - //anything in OSX tmp dirs need a wildcard - //to work with fsevents - var glob = output.tmp ? path.join(output.dir, '*.js') : output.from - //TODO: get rid of this since it introduces some confusion // (esp. related to 'serving dir' and 'output dir' which may be different) //add the uri / output to budo instance @@ -161,12 +156,10 @@ module.exports = function() { host: hostname, server: server, 'live-port': opt['live-port'] - }, output, { - glob: glob - }) + }, output) //defaults for live() / watch() functions - defaultGlobs = ['**/*.{html,css}', emitter.glob] + defaultGlobs = ['**/*.{html,css}'] //watchify@3 has some extra options defaultWatchOpts = { poll: opt.poll, diff --git a/lib/watchify.js b/lib/watchify.js index 59c6dc5..6d1825c 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -53,16 +53,17 @@ module.exports = function(watchifyArgs, opt) { update() return } - + var didError = false var outStream = fs.createWriteStream(opt.outfile) var basename = path.basename(opt.outfile) var wb = watchify.bundle() wb.on('error', function(err) { - console.error(String(err)) + err = String(err) + console.error(err) didError = true - outStream.end('console.error(' + JSON.stringify(String(err)) + ');') + outStream.end('console.error(' + JSON.stringify(err) + ');') }) wb.pipe(outStream) From 9e895df2b106d56a2412cf09e6ab7ea5a8571436 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 3 Apr 2015 20:16:21 -0400 Subject: [PATCH 052/302] splitting out some modules --- example/app.js | 26 +++++++++++++------------- lib/get-module.js | 23 +++++++++-------------- lib/get-watchify.js | 7 ++++--- lib/watchify.js | 10 ++++++++-- package.json | 1 + 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/example/app.js b/example/app.js index d699ecc..19a07ef 100644 --- a/example/app.js +++ b/example/app.js @@ -5,8 +5,8 @@ var fit = require('canvas-fit')(ctx.canvas, window, dpr) //setup canvas DOM state window.addEventListener('resize', fit, false) require('domready')(function() { - fit() - document.body.appendChild(ctx.canvas) + fit() + document.body.appendChild(ctx.canvas) }) var img = new Image() @@ -14,17 +14,17 @@ img.src = 'baboon.png' var time = 0 require('raf-loop')(function(dt) { - var width = ctx.canvas.width, - height = ctx.canvas.height - ctx.clearRect(0, 0, width, height) + var width = ctx.canvas.width, + height = ctx.canvas.height + ctx.clearRect(0, 0, width, height) - time += dt/1000 + time += dt/1000 - ctx.save() - ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 200, 500, 105) - ctx.fillText("from browserify!", 40, 40) - if (img.width > 0 || img.height > 0) - ctx.drawImage(img, 50, 50) - ctx.restore() + ctx.save() + ctx.scale(dpr, dpr) + ctx.fillRect(Math.sin(time)*50 + 300, 50, 100, 100) + ctx.fillText("from browserify!", 40, 40) + if (img.width > 0 || img.height > 0) + ctx.drawImage(img, 50, 50) + ctx.restore() }).start() \ No newline at end of file diff --git a/lib/get-module.js b/lib/get-module.js index 23ab248..da55f58 100644 --- a/lib/get-module.js +++ b/lib/get-module.js @@ -1,12 +1,10 @@ //finds the directory of a CLI module by name //searches local first, then global - var find = require('find-global-packages') var resolve = require('resolve') var path = require('path') var which = require('npm-which') var fs = require('fs') -var findParent = require('find-parent-dir') module.exports = function(name, opt, cb) { resolve(name, opt, function (err, result) { @@ -24,9 +22,9 @@ module.exports = function(name, opt, cb) { return path.basename(dir) === name }) - if (results.length === 0) { + if (results.length === 0) return npmWhich() - } else + else return cb(null, results[0]) } @@ -35,16 +33,18 @@ module.exports = function(name, opt, cb) { which(opt.basedir)(name, function(err, bin) { if (err) return cb(bail()) - //assume symlink, get real target + + //assume its a symlink and get the real target fs.realpath(bin, function(err, link) { if (err) return cb(bail()) - //walk upward until we hit the folder by name + //now walk upward until we hit the folder by name var binPath = getBinPath(link) if (binPath) - return cb(null, binPath) - return cb(bail()) + cb(null, binPath) + else + cb(bail()) }) }) } @@ -67,11 +67,6 @@ module.exports = function(name, opt, cb) { } function bail() { - var msg = [ - '"'+name+'" is not installed globally or locally', - 'Example:', - ' npm install watchify --save-dev\n' - ].join('\n') - return new Error(msg) + return new Error('"'+name+'" is not installed globally or locally') } } \ No newline at end of file diff --git a/lib/get-watchify.js b/lib/get-watchify.js index db12435..6b3d6fb 100644 --- a/lib/get-watchify.js +++ b/lib/get-watchify.js @@ -1,5 +1,5 @@ var resolve = require('resolve') -var getModule = require('./get-module') +var getModule = require('resolve-npm-which') //finds watchify/bin/args.js local or global module.exports = function getWatchify(opt, cb) { @@ -11,10 +11,11 @@ module.exports = function getWatchify(opt, cb) { if (err) return cb(err) - resolve('watchify/bin/args.js', { basedir: result }, resolved) + //Once we get watchify, we also need to grab its bin/args! + resolve('./bin/args.js', { basedir: result }, resolved) function resolved(err, watchifyModule) { if (err) - return cb(err) + return cb(new Error('could not find watchify/bin/args.js - unsupported watchify version')) var fromArgs = require(watchifyModule) cb(null, fromArgs) } diff --git a/lib/watchify.js b/lib/watchify.js index 6d1825c..5c2cebe 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -26,8 +26,14 @@ module.exports = function(watchifyArgs, opt) { } getWatchify({ basedir: opt.dir }, function(err, fromArgs) { - if (err) - return emitter.emit('error', err) + if (err) { + var msg = [ + err.message, + 'Example:', + ' npm install watchify --save-dev\n' + ].join('\n') + return emitter.emit('error', new Error(msg)) + } if (closed) return diff --git a/package.json b/package.json index 8edc7ce..6340829 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "npm-execspawn": "^1.0.6", "npm-which": "^2.0.0", "resolve": "^1.1.6", + "resolve-npm-which": "^1.0.1", "response-stream": "0.0.0", "rimraf": "^2.2.8", "routes-router": "^4.1.2", From 67af3b0cfe81d68d3ffd687b9cc8396e01d82756 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 17:26:51 -0400 Subject: [PATCH 053/302] splitting out module --- lib/get-module.js | 72 ----------------------------------------------- 1 file changed, 72 deletions(-) delete mode 100644 lib/get-module.js diff --git a/lib/get-module.js b/lib/get-module.js deleted file mode 100644 index da55f58..0000000 --- a/lib/get-module.js +++ /dev/null @@ -1,72 +0,0 @@ -//finds the directory of a CLI module by name -//searches local first, then global -var find = require('find-global-packages') -var resolve = require('resolve') -var path = require('path') -var which = require('npm-which') -var fs = require('fs') - -module.exports = function(name, opt, cb) { - resolve(name, opt, function (err, result) { - if (err) - find(onGlobals) - else - cb(null, path.dirname(result)) - }) - - function onGlobals(err, dirs) { - if (err) //problem finding globals - return npmWhich() - - var results = dirs.filter(function(dir) { - return path.basename(dir) === name - }) - - if (results.length === 0) - return npmWhich() - else - return cb(null, results[0]) - } - - function npmWhich() { - //last resort, e.g. /Users/username/npm/bin/watchify - which(opt.basedir)(name, function(err, bin) { - if (err) - return cb(bail()) - - //assume its a symlink and get the real target - fs.realpath(bin, function(err, link) { - if (err) - return cb(bail()) - - //now walk upward until we hit the folder by name - var binPath = getBinPath(link) - if (binPath) - cb(null, binPath) - else - cb(bail()) - }) - }) - } - - function getBinPath(file) { - var found, - last - while (file !== last) { - var base = path.basename(file) - if (base === 'node_modules') - break - if (base === name) { - found = file - break - } - last = file - file = path.resolve(file, '..') - } - return found - } - - function bail() { - return new Error('"'+name+'" is not installed globally or locally') - } -} \ No newline at end of file From dc6edadbfce48f4e1084e544c0a91b2a93ec5e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=CD=92=CD=8B=CD=A5=CD=AD=CC=8F=CD=92=CD=9B=CD=9E=CC=AC?= =?UTF-8?q?=CC=AF=CD=96=CC=AD=CC=97=CD=87=CD=88=CD=99=CD=99=CC=96=CC=A5?= =?UTF-8?q?=C3=B4=CC=92=CD=86=CD=86=CD=91=CC=8F=CD=AB=CD=AA=CD=A4=CD=A5?= =?UTF-8?q?=CD=A7=CC=80=CD=92=CC=80=CC=B9=CC=97=CC=9E=CD=88=CD=93=CC=A5s?= =?UTF-8?q?=CC=93=CC=86=CD=AF=CD=A4=CD=AA=CD=92=CD=AD=CD=91=CC=88=CD=98?= =?UTF-8?q?=CD=89=CC=A6=CC=98=CC=98=CC=AA=CC=B1=CD=89=CC=B0=CC=A6h=CC=8B?= =?UTF-8?q?=CD=8A=CD=82=CD=8C=CD=90=CD=A3=CC=8B=CC=93=CD=8C=CC=92=CD=82?= =?UTF-8?q?=CD=90=CD=92=CC=86=CC=A8=CC=A4=CC=AB=CD=85=CD=89u=CC=92=CC=81?= =?UTF-8?q?=CD=AE=CC=93=CD=8B=CC=90=CD=AB=CD=92=CC=93=CC=8D=CD=AC=CD=AC?= =?UTF-8?q?=CC=91=CD=A4=CC=88=CD=A4=CD=A7=CD=98=CC=AEa=CD=8B=CC=8C=CC=94?= =?UTF-8?q?=CC=92=CC=92=CD=A9=CC=81=CD=AD=CC=8A=CC=81=CC=82=CD=97=CD=A0?= =?UTF-8?q?=CD=95=CC=BB=CC=BB=CC=A0=CC=B2=CD=99=20=CC=84=CD=92=CC=90=CC=90?= =?UTF-8?q?=CC=82=CC=8B=CC=83=CC=8E=CC=8D=CD=A6=CC=BF=CC=9B=CC=AC=CC=9E?= =?UTF-8?q?=CC=B2=CD=95=CC=AD=CC=A4=CD=95W=CD=AA=CC=90=CD=82=CC=86=CD=AA?= =?UTF-8?q?=CC=80=CD=A7=CC=8C=CD=A5=CC=85=CC=9A=CD=AD=CC=93=CD=9D=CC=97?= =?UTF-8?q?=CD=85=CC=A4=CC=9C=CC=9Cu=CC=87=CD=A8=CD=AA=CC=8E=CD=92=CD=8B?= =?UTF-8?q?=CC=88=CD=AD=CC=93=CC=83=CC=BD=CD=AC=CD=AA=CC=9A=CC=A7=CC=B1?= =?UTF-8?q?=CC=A6=CC=A3=CD=8Dy=CC=94=CC=91=CC=8B=CD=97=CC=90=CC=BD=CD=A6?= =?UTF-8?q?=CD=82=CC=9A=CC=90=CC=88=CD=8B=CD=A3=CC=81=CD=A7=CD=8B=CC=83?= =?UTF-8?q?=CD=A5=CD=98t=CC=8F=CD=A6=CD=9B=CC=81=CC=9A=CC=91=CC=87=CD=9E?= =?UTF-8?q?=CC=96=CC=AD=CC=BC=CC=A9=CC=9D=CC=BA=CC=9E=CC=9C=CC=AC=CD=8D?= =?UTF-8?q?=CC=A9s=CC=92=CC=8F=CC=8F=CC=80=CC=83=CC=82=CC=BF=CD=AF=CD=9B?= =?UTF-8?q?=CD=92=CD=A9=CC=84=CC=82=CD=AF=CC=86=CC=8C=CD=AE=CC=92=CD=AD?= Date: Sun, 5 Apr 2015 15:22:13 -0700 Subject: [PATCH 054/302] docs: fix ndjson link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 600f652..2d2e4b0 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ budo index.js --live --transform brfs You can open `localhost:9966` to see the content in action. -To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](ndjson.org)-based stream can be used. +To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used. ```sh budo index.js -o bundle.js | garnish From 8896e97e92476c9665f4ebff858a1086de14f0a6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 19:18:54 -0400 Subject: [PATCH 055/302] more work on watchify API --- README.md | 14 +++--- example/app.js | 2 +- index.js | 61 ++++++++++---------------- lib/budo.js | 84 +++++++++++++++++------------------- lib/get-output.js | 46 ++++++-------------- lib/get-watchify.js | 7 ++- lib/server.js | 43 ++++++++++--------- lib/tinylr.js | 2 +- lib/watchify.js | 18 +++++--- package.json | 3 +- test/test-api.js | 102 +++++++++++++++++++------------------------- 11 files changed, 163 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index 600f652..30ff290 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mattdesl/budo) -This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but with a stronger focus on incremental bundling, LiveReload (including CSS injection), and other [experimental features](#script-injection) down the road. +This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but specifically focused on incremental reloading and LiveReload integration (including CSS injection). Note that budo needs a copy of `watchify` installed. It can be either local (preferred) or global. @@ -10,16 +10,16 @@ Note that budo needs a copy of `watchify` installed. It can be either local (pre npm install budo watchify -g ``` -The simplest use cases will start up a server with a default `index.html` and incrementally bundle your source on filesave. Examples: +The simplest use cases will start up a server with a default `index.html` and incrementally bundle your source on filesave. The requests are delayed until the bundle has finished, so you aren't served stale or empty bundles. Examples: ```sh #run watchify on port 9966 budo index.js #run watchify with explicit output file -budo index.js --outfile bundle.js --verbose +budo index.js --verbose -#run watchify with some options and trigger LiveReload on change +#run watchify with some options and trigger LiveReload on file change budo index.js --live --transform brfs ``` @@ -28,7 +28,7 @@ You can open `localhost:9966` to see the content in action. To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](ndjson.org)-based stream can be used. ```sh -budo index.js -o bundle.js | garnish +budo index.js -v | garnish ``` See [docs](#docs) for more features. @@ -59,7 +59,6 @@ Usage: Options: --help, -h show help message - --outfile, -o path to output bundle --port the port to run, default 9966 --host the host, default "localhost" --dir the directory to serve, and the base for --outfile @@ -70,13 +69,10 @@ Options: By default, the `--debug` option will be sent to watchify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. -*Note:* The `--outfile` is relative to the specified `--dir`. - ### API The API mirrors the CLI except you must provide a `stream` for logging, and it does not attempt to auto-portfind. - ```js var budo = require('budo') diff --git a/example/app.js b/example/app.js index 19a07ef..4ef0974 100644 --- a/example/app.js +++ b/example/app.js @@ -7,7 +7,7 @@ window.addEventListener('resize', fit, false) require('domready')(function() { fit() document.body.appendChild(ctx.canvas) -}) +}) var img = new Image() img.src = 'baboon.png' diff --git a/index.js b/index.js index 14b1821..e0dea08 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var assign = require('xtend/mutable') var Emitter = require('events/') var getOutput = require('./lib/get-output') var rimraf = require('rimraf') +var path = require('path') var budo = require('./lib/budo') @@ -19,7 +20,6 @@ module.exports = function(entry, opts) { } var emitter = budo() - emitter.on('connect', setupLive) var entries = Array.isArray(entry) ? entry : [entry] entries = entries.filter(Boolean) @@ -30,36 +30,18 @@ module.exports = function(entry, opts) { argv.port = typeof argv.port === 'number' ? argv.port : 9966 argv.dir = argv.dir || process.cwd() - var outOpts = xtend(argv, { - __to: entryMapping() - }) - - getOutput(outOpts, function(err, output) { - if (err) { - if (err.name === 'OUTPIPE') - bail("outpipe with --outfile currently not supported") - else - bail("Could not create temp bundle.js directory") - return emitter - } - - var tmp = output.tmp - var tmpFile = output.from - - //run watchify server - emitter._start(entries, output, argv) - .on('exit', function() { - log.info('closing') - if (tmp && tmpFile) { - //last attempt to remove the temporary bundle file - rimraf(tmpFile, function(err) { - if (err) - log.debug('error cleaning up temp file', err) - }) - } - }) - }) - + + var outfile = argv.o || argv.outfile + argv.from = entries[0] + argv.to = path.basename(entries[0]) + + //run watchify server + emitter.on('connect', setupLive) + emitter._start(entries, argv) + .on('exit', function() { + log.info('closing') + }) + return emitter //if user requested live: true, set it up with some defaults @@ -67,21 +49,22 @@ module.exports = function(entry, opts) { if (argv.live || argv['live-plugin']) { emitter .watch() - .live({ - host: argv.host, - port: argv['live-port'] - }) - .on('watch', function(ev, file) { - if (ev === 'change' || ev === 'add') { + .live() + .on('watch', function(ev, file) { + //HTML/CSS changes + if (ev === 'change' || ev === 'add') emitter.reload(file) - } }) .on('update', function(file) { - emitter.reload(file) + console.log("Update event") + //bundle.js changes + emitter.reload() }) } } + + function entryMapping() { var to var first = entries[0] diff --git a/lib/budo.js b/lib/budo.js index f2f1821..b2114e8 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -25,14 +25,10 @@ module.exports = function() { var defaultWatchOpts var deferreds = [] - emitter._start = function(entries, output, opt) { + emitter._start = function(entries, opt) { opt = opt || {} var port = opt.port - var serverOpt = xtend(opt, { - output: output - }) - - server = http(serverOpt) + server = http(opt) .on('error', function(err) { emitter.emit('error', err) }) @@ -42,11 +38,27 @@ module.exports = function() { return } - //setup "event" param for callback - assignOptions(output, opt) + var hostname = (opt.host || 'localhost') + var uri = "http://" + hostname + ":" + opt.port + "/" + log.info("Server running at", uri) + + //defaults for watch() function + defaultGlobs = ['**/*.{html,css}'] + + //watchify@3 has some extra options + defaultWatchOpts = { + poll: opt.poll, + 'ignore-watch': typeof opt['ignore-watch'] === 'undefined' ? opt.iw : opt['ignore-watch'] + } + //and default live options + defaultLiveOpts = { + plugin: opt['live-plugin'], + host: opt.host, + port: emitter['live-port'] + } //start watchify process - runWatchify(entries, output, opt) + runWatchify(entries, opt) started = true @@ -56,8 +68,16 @@ module.exports = function() { }) deferreds.length = 0 - //finally, emit callback - emitter.emit('connect', emitter) + //finally, emit callback with some info + emitter.emit('connect', { + uri: uri, + port: opt.port, + host: hostname, + to: opt.to, + from: opt.from, + dir: opt.dir, + 'live-port': opt['live-port'] + }) }) return emitter } @@ -118,7 +138,7 @@ module.exports = function() { return emitter - function runWatchify(entries, output, opt) { + function runWatchify(entries, opt) { if (closed) return @@ -128,49 +148,21 @@ module.exports = function() { //create a new watchify instance watchify = createWatchify(watchifyArgs, { dir: opt.dir, + from: opt.from, + to: opt.to, verbose: opt.v || opt.verbose, - delay: typeof opt.delay === 'number' ? opt.delay : DEFAULT_DELAY, - outfile: output.from + delay: typeof opt.delay === 'number' ? opt.delay : DEFAULT_DELAY }) .on('pending', function() { if (server) server.pending() }) - .on('update', function() { - if (server) server.update() - emitter.emit('update', output.from) + .on('update', function(contents) { + if (server) server.update(contents) + emitter.emit('update') }) .on('error', emitter.emit.bind(emitter, 'error')) } - function assignOptions(output, opt) { - var hostname = (opt.host || 'localhost') - var uri = "http://" + hostname + ":" + opt.port + "/" - log.info("Server running at", uri) - - //TODO: get rid of this since it introduces some confusion - // (esp. related to 'serving dir' and 'output dir' which may be different) - //add the uri / output to budo instance - assign(emitter, { - uri: uri, - port: opt.port, - host: hostname, - server: server, - 'live-port': opt['live-port'] - }, output) - - //defaults for live() / watch() functions - defaultGlobs = ['**/*.{html,css}'] - //watchify@3 has some extra options - defaultWatchOpts = { - poll: opt.poll, - 'ignore-watch': typeof opt['ignore-watch'] === 'undefined' ? opt.iw : opt['ignore-watch'] - } - defaultLiveOpts = { - plugin: opt['live-plugin'], - host: opt.host, - port: emitter['live-port'] - } - } function noop() { return emitter diff --git a/lib/get-output.js b/lib/get-output.js index 76e3dce..64174bc 100644 --- a/lib/get-output.js +++ b/lib/get-output.js @@ -5,41 +5,21 @@ var quote = require('shell-quote') //get an output directory, from user or tmp dir module.exports = function getOutput(argv, cb) { var outfile = argv.o || argv.outfile - if (!outfile) { - //user can specify a mapping for temp dirs - var bundleTo = argv.__to || 'bundle.js' + var from - tmpdir(function(err, filedir) { - var output - if (!err) { - var file = path.join(filedir, bundleTo) - output = { - tmp: true, - from: file, - to: bundleTo, - dir: filedir - } - } - cb(err, output) - }) + //outpipe was added in watchify@3.1.0 + if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { + var err = new Error("unsupported outpipe command") + err.name = 'OUTPIPE' + return cb(err) } else { - var outpipe = null - var from - - //outpipe was added in watchify@3.1.0 - if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { - var err = new Error("unsupported outpipe command") - err.name = 'OUTPIPE' - return cb(err) - } else { - from = path.join(argv.dir, outfile) - } + from = path.join(argv.dir, outfile) + } - cb(null, { - from: from, - to: outfile, - outpipe: outpipe, - dir: argv.dir - }) + return { + from: from, + to: outfile, + outpipe: outpipe, + dir: argv.dir } } \ No newline at end of file diff --git a/lib/get-watchify.js b/lib/get-watchify.js index 6b3d6fb..edb2ef4 100644 --- a/lib/get-watchify.js +++ b/lib/get-watchify.js @@ -11,13 +11,12 @@ module.exports = function getWatchify(opt, cb) { if (err) return cb(err) - //Once we get watchify, we also need to grab its bin/args! - resolve('./bin/args.js', { basedir: result }, resolved) - function resolved(err, watchifyModule) { + //Once we get watchify path, grab its bin/args.js + resolve('./bin/args.js', { basedir: result }, function(err, watchifyModule) { if (err) return cb(new Error('could not find watchify/bin/args.js - unsupported watchify version')) var fromArgs = require(watchifyModule) cb(null, fromArgs) - } + }) }) } \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 73b7e45..834e25f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -23,11 +23,12 @@ module.exports.http = function(opts) { } }) - server.update = function() { + server.update = function(contents) { handler.pending = false + handler.contents = contents handler.emit('update') } - + server.pending = function() { handler.pending = true } @@ -44,6 +45,7 @@ module.exports.static = function(opts) { emitter.live = opts.live emitter.router = router emitter.pending = false + emitter.contents = '' var previous = noop @@ -52,15 +54,9 @@ module.exports.static = function(opts) { port: opts['live-port'] } - var out = opts.output var entryHandler = staticHandler - if (out.tmp) { - entryHandler = ecstatic({ - root: out.dir - }) - } - router.addRoute('/' + out.to, function(req, res, params) { + router.addRoute('/' + opts.to, function(req, res, params) { log.info({ url: req.url, type: 'static' @@ -68,14 +64,13 @@ module.exports.static = function(opts) { if (emitter.pending) { console.log("Bundle pending...") - // emitter.removeAllListeners('update') emitter.once('update', function() { console.log("Bundle updated...") - entryHandler(req, res) + submit(req, res) }) } else { console.log("Bundle ready...") - entryHandler(req, res) + submit(req, res) } }) @@ -83,30 +78,36 @@ module.exports.static = function(opts) { router.addRoute('/', home) router.addRoute('*', function(req, res, params) { - // log.info({ - // url: req.url, - // type: 'static' - // }) + log.info({ + url: req.url, + type: 'static' + }) staticHandler(req, res) }) return emitter + function submit(req, res) { + res.setHeader('content-type', 'text/javascript') + res.end(emitter.contents) + } + function home(req, res, params) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed if (emitter.live) res = inject(res, liveOpts) + var type = exists ? 'static' : 'generated' - // log.info({ - // url: req.url, - // type: type - // }) + log.info({ + url: req.url, + type: type + }) if (exists) staticHandler(req, res) else - generateIndex(out.to, req, res) + generateIndex(opts.to, req, res) }) } diff --git a/lib/tinylr.js b/lib/tinylr.js index 5d77080..0cf8783 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -40,7 +40,7 @@ module.exports = function(opt) { try { server.changed({ body: { - files: [path] + files: path ? [ path ] : '*' } }) } catch (e) { diff --git a/lib/watchify.js b/lib/watchify.js index 5c2cebe..8abf23a 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -6,6 +6,7 @@ var Emitter = require('events/') var debounce = require('debounce') var fs = require('fs') var path = require('path') +var concat = require('concat-stream') module.exports = function(watchifyArgs, opt) { var emitter = new Emitter() @@ -16,6 +17,7 @@ module.exports = function(watchifyArgs, opt) { var time = Date.now() var first = true + var contents = null emitter.close = function() { if (closed) @@ -61,8 +63,11 @@ module.exports = function(watchifyArgs, opt) { } var didError = false - var outStream = fs.createWriteStream(opt.outfile) - var basename = path.basename(opt.outfile) + + var outStream = concat(function(body) { + contents = body + bundleEnd() + }) var wb = watchify.bundle() wb.on('error', function(err) { @@ -77,24 +82,25 @@ module.exports = function(watchifyArgs, opt) { console.error(err) update() }) - outStream.on('close', function() { + + function bundleEnd() { if (opt.verbose && !didError) { var delay = Date.now() - time log.info({ elapsed: (delay / 1000).toFixed(2) + ' s', type: 'bundle', - url: basename + url: opt.from }) } update() - }) + } } return emitter function update() { if (pending) { pending = false - emitter.emit('update') + emitter.emit('update', contents) } } } \ No newline at end of file diff --git a/package.json b/package.json index 6340829..2db2e7b 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "bole": "^2.0.0", "chokidar": "^1.0.0-rc5", + "concat-stream": "^1.4.8", "dargs": "^4.0.0", "debounce": "^1.0.0", "ecstatic": "^0.5.8", @@ -60,7 +61,7 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/app.js -o bundle.js --dir example --verbose | garnish", + "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", "live": "./bin/cmd.js example/app.js --dir example --live | garnish", "uglify": "./bin/cmd.js example/app.js -v --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", diff --git a/test/test-api.js b/test/test-api.js index 0c1ad4e..b230001 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -3,14 +3,13 @@ var budo = require('../') var cleanup = require('./cleanup') var path = require('path') -// test('sets watch() after connect', function(t) { -// t.plan(9) +// test('gets update() after connect', function(t) { +// t.plan(8) // t.timeoutAfter(10000) // budo('test/app.js', { // dir: __dirname, -// port: 8000, -// outfile: 'bundle.js' +// port: 8000 // }) // .on('error', function(err) { // t.fail(err) @@ -22,13 +21,11 @@ var path = require('path') // t.equal(app.uri, 'http://localhost:8000/', 'uri matches') // t.equal(app.host, 'localhost', 'host is not specified') // t.equal(app.port, 8000, 'port matches') -// t.equal(app.glob, file, 'glob matches file') // t.equal(app.dir, __dirname, 'dir matches') // app -// .watch() -// .once('watch', function(type) { -// t.ok(true, 'got watch') +// .once('update', function(type) { +// t.ok(true, 'got bundle.js update') // app.close() // cleanup() // }) @@ -41,62 +38,51 @@ var path = require('path') // }) // }) -// test('sets watch() with args before connect', function(t) { -// t.plan(3) -// t.timeoutAfter(10000) - -// var app = budo('test/app.js', { -// dir: __dirname, -// port: 8000, -// outfile: 'bundle.js' -// }) -// .watch() //enable watcher -// .once('watch', function() { -// t.ok(true, 'got watch') -// app.close() -// cleanup() -// }) -// .on('error', function(err) { -// t.fail(err) -// }) -// .on('reload', function() { -// t.fail('should not have received reload event') -// }) -// .on('connect', function(app) { -// var file = path.join(__dirname, 'bundle.js') -// t.equal(app.from, file, 'from matches') -// }) -// .on('exit', function() { -// t.ok(true, 'closing') -// }) -// }) - -test('sets watch() and live() by default with live: true', function(t) { +test('sets live() with args before connect', function(t) { t.plan(4) t.timeoutAfter(10000) var app = budo('test/app.js', { dir: __dirname, port: 8000, - live: true, outfile: 'bundle.js' }) - .once('reload', function(err) { - t.ok(true, 'got reload event') - app.close() - cleanup() - }) - .once('watch', function() { - t.ok(true, 'got watch event') - }) - .on('error', function(err) { - t.fail(err) - }) - .on('connect', function(app) { - var file = path.join(__dirname, 'bundle.js') - t.equal(app.from, file, 'from matches') - }) - .on('exit', function() { - t.ok(true, 'closing') + //manually enable LiveReload server + .live() + //manually trigger LiveReload event on bundle.js update + .on('update', function() { + t.ok('got live reload event') + app.reload() + }) + testLive(t, app) +}) + +test('sets watch() and live() by default with live: true', function(t) { + t.plan(3) + t.timeoutAfter(10000) + + var app = budo('test/app.js', { + dir: __dirname, + port: 8000, + live: true }) -}) \ No newline at end of file + testLive(t, app) +}) + +function testLive(t, app) { + app + .once('update', function() { //bundle.js changed + t.ok(true, 'got update event') + }) + .once('reload', function(err) { //LiveReload triggered + t.ok(true, 'got reload event') + app.close() + cleanup() + }) + .on('error', function(err) { + t.fail(err) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +} \ No newline at end of file From 1a58b587669f52d1b34e9f501b19ed3f9d853f88 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 19:19:19 -0400 Subject: [PATCH 056/302] on second thought; lets not go to gitter. tis a silly place --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2d2e4b0..e299157 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # budō -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mattdesl/budo) - This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but with a stronger focus on incremental bundling, LiveReload (including CSS injection), and other [experimental features](#script-injection) down the road. Note that budo needs a copy of `watchify` installed. It can be either local (preferred) or global. From a11f4a04ea14bd9645eebf9bde6822fabc7986ff Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 19:20:06 -0400 Subject: [PATCH 057/302] good ol' fashioned stability badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e299157..5f26729 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # budō +[![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) + This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but with a stronger focus on incremental bundling, LiveReload (including CSS injection), and other [experimental features](#script-injection) down the road. Note that budo needs a copy of `watchify` installed. It can be either local (preferred) or global. From 6fdc8770bb6259f4140b21c308f17cc3c27e1789 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 21:34:43 -0400 Subject: [PATCH 058/302] fixing tests, reworking some stuff --- example/other.js | 1 + index.js | 40 ++++++++----- lib/budo.js | 23 ++++--- lib/file-watch.js | 14 ++++- lib/server.js | 10 ++-- lib/tinylr.js | 26 ++++---- lib/watchify.js | 15 +++-- test/test-api.js | 113 ++++++++++++++++++++--------------- test/test-close-immediate.js | 46 ++++++++++++++ test/test-close.js | 78 ------------------------ test/test-file-watch.js | 11 ++++ test/test-live.js | 68 +++++++++++++-------- 12 files changed, 249 insertions(+), 196 deletions(-) create mode 100644 example/other.js create mode 100644 test/test-close-immediate.js delete mode 100644 test/test-close.js create mode 100644 test/test-file-watch.js diff --git a/example/other.js b/example/other.js new file mode 100644 index 0000000..25044f0 --- /dev/null +++ b/example/other.js @@ -0,0 +1 @@ +console.log("FOO") \ No newline at end of file diff --git a/index.js b/index.js index e0dea08..bdd3902 100644 --- a/index.js +++ b/index.js @@ -28,13 +28,28 @@ module.exports = function(entry, opts) { return emitter } + + //e.g. + //clean up entries and take the first one for bundle mapping + var file + entries = entries.map(function(entry, i) { + var map = mapping(entry) + if (i === 0) + file = map.to + return map.from + }) + + //if user specified -o use that as our entry map + var outfile = argv.serve + if (outfile && typeof outfile === 'string') + file = outfile + argv.port = typeof argv.port === 'number' ? argv.port : 9966 argv.dir = argv.dir || process.cwd() - var outfile = argv.o || argv.outfile - argv.from = entries[0] - argv.to = path.basename(entries[0]) - + var map = mapping(entries[0]) + argv.serve = file + //run watchify server emitter.on('connect', setupLive) emitter._start(entries, argv) @@ -56,25 +71,18 @@ module.exports = function(entry, opts) { emitter.reload(file) }) .on('update', function(file) { - console.log("Update event") //bundle.js changes - emitter.reload() + emitter.reload(file) }) } } - - - function entryMapping() { - var to - var first = entries[0] - var parts = first.split(':') + function mapping(entry) { + var parts = entry.split(':') if (parts.length > 1 && parts[1].length > 0) { - var from = parts[0] - to = parts[1] - entries[0] = from + return { from: parts[0], to: parts[1] } } - return to + return { from: entry, to: 'bundle.js' } } function bail(msg) { diff --git a/lib/budo.js b/lib/budo.js index b2114e8..813830c 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -15,7 +15,8 @@ module.exports = function() { var emitter = new Emitter() var started = false var closed = false - + var serveAs + var watchify var server var watcher @@ -59,7 +60,8 @@ module.exports = function() { //start watchify process runWatchify(entries, opt) - + + serveAs = opt.serve started = true //if user wanted live() or watch() enabled @@ -73,8 +75,8 @@ module.exports = function() { uri: uri, port: opt.port, host: hostname, - to: opt.to, - from: opt.from, + serve: opt.serve, + entries: entries, dir: opt.dir, 'live-port': opt['live-port'] }) @@ -109,6 +111,7 @@ module.exports = function() { server._live = !liveOpt.plugin tinylr = createTinylr(xtend(defaultLiveOpts, liveOpt)) emitter.reload = function(path) { + path = path tinylr.reload(path) emitter.emit('reload', path) } @@ -136,6 +139,7 @@ module.exports = function() { return emitter } + return emitter function runWatchify(entries, opt) { @@ -148,17 +152,18 @@ module.exports = function() { //create a new watchify instance watchify = createWatchify(watchifyArgs, { dir: opt.dir, - from: opt.from, - to: opt.to, + serve: opt.serve, verbose: opt.v || opt.verbose, delay: typeof opt.delay === 'number' ? opt.delay : DEFAULT_DELAY }) .on('pending', function() { - if (server) server.pending() + if (server) + server.pending() }) .on('update', function(contents) { - if (server) server.update(contents) - emitter.emit('update') + if (server) + server.update(contents) + emitter.emit('update', serveAs) }) .on('error', emitter.emit.bind(emitter, 'error')) } diff --git a/lib/file-watch.js b/lib/file-watch.js index 3aaa995..82001d5 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -17,10 +17,18 @@ module.exports = function(glob, opt) { }, opt) var emitter = new Emitter() + var closed = false, ready = false watcher = watch(glob, opt) watcher.on('add', reload.bind(null, 'add')) watcher.on('change', reload.bind(null, 'change')) + + // chokidar@1.0.0-r6 only allows close after ready event + watcher.once('ready', function() { + ready = true + if (closed) + watcher.close() + }) function reload(event, path) { emitter.emit('watch', event, path) @@ -31,7 +39,11 @@ module.exports = function(glob, opt) { } emitter.close = function() { - watcher.close() + if (closed) + return + if (ready) + watcher.close() + closed = true } return emitter } diff --git a/lib/server.js b/lib/server.js index 834e25f..066d145 100644 --- a/lib/server.js +++ b/lib/server.js @@ -11,7 +11,7 @@ var Emitter = require('events/') var noop = function(){} module.exports.http = function(opts) { - var handler = module.exports.static(opts) + var handler = createHandler(opts) var server = http.createServer(handler.router) Object.defineProperty(server, '_live', { @@ -36,7 +36,7 @@ module.exports.http = function(opts) { return server } -module.exports.static = function(opts) { +function createHandler(opts) { var basedir = opts.dir || process.cwd() var staticHandler = ecstatic(basedir) var router = Router() @@ -47,8 +47,6 @@ module.exports.static = function(opts) { emitter.pending = false emitter.contents = '' - var previous = noop - var liveOpts = { host: opts.host, port: opts['live-port'] @@ -56,7 +54,7 @@ module.exports.static = function(opts) { var entryHandler = staticHandler - router.addRoute('/' + opts.to, function(req, res, params) { + router.addRoute('/' + opts.serve, function(req, res, params) { log.info({ url: req.url, type: 'static' @@ -107,7 +105,7 @@ module.exports.static = function(opts) { if (exists) staticHandler(req, res) else - generateIndex(opts.to, req, res) + generateIndex(opts.serve, req, res) }) } diff --git a/lib/tinylr.js b/lib/tinylr.js index 0cf8783..ae5f7ad 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -10,11 +10,13 @@ module.exports = function(opt) { opt.port = 35729 var server = tinylr() - var closed = false + var closed = false, ready = false server.listen(opt.port, opt.host, function(a) { - if (closed) - return + ready = true + if (closed) + return server.close() + log.info('livereload running on ' + opt.port) }) @@ -23,18 +25,20 @@ module.exports = function(opt) { serverImpl.on('error', function(err) { if (err.code === 'EADDRINUSE') { process.stderr.write('ERROR: livereload not started, port ' + opt.port + ' is in use\n') - server.close() - closed = true + close() } }) - return { - close: function close() { - if (closed) - return + function close() { + if (closed) + return + closed = true + if (ready) server.close() - closed = true - }, + } + + return { + close: close, reload: function reload(path) { try { diff --git a/lib/watchify.js b/lib/watchify.js index 8abf23a..da42cdf 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -23,8 +23,15 @@ module.exports = function(watchifyArgs, opt) { if (closed) return closed = true - if (watchify) - watchify.close() + emitter.removeAllListeners('update') + if (watchify) { + //needed for watchify@3.0.0 + //see test-close-immediate + //this needs to be revisited upstream + setTimeout(function() { + watchify.close() + }, 100) + } } getWatchify({ basedir: opt.dir }, function(err, fromArgs) { @@ -58,7 +65,7 @@ module.exports = function(watchifyArgs, opt) { function bundle() { if (closed) { - update() + process.nextTick(update) return } @@ -89,7 +96,7 @@ module.exports = function(watchifyArgs, opt) { log.info({ elapsed: (delay / 1000).toFixed(2) + ' s', type: 'bundle', - url: opt.from + url: opt.serve }) } update() diff --git a/test/test-api.js b/test/test-api.js index b230001..1132156 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -3,61 +3,78 @@ var budo = require('../') var cleanup = require('./cleanup') var path = require('path') -// test('gets update() after connect', function(t) { -// t.plan(8) -// t.timeoutAfter(10000) +test('gets connect info', function(t) { + t.plan(7) + t.timeoutAfter(10000) -// budo('test/app.js', { -// dir: __dirname, -// port: 8000 -// }) -// .on('error', function(err) { -// t.fail(err) -// }) -// .on('connect', function(app) { -// var file = path.join(__dirname, 'bundle.js') -// t.equal(app.to, 'bundle.js', 'mapping matches') -// t.equal(app.from, file, 'from matches') -// t.equal(app.uri, 'http://localhost:8000/', 'uri matches') -// t.equal(app.host, 'localhost', 'host is not specified') -// t.equal(app.port, 8000, 'port matches') -// t.equal(app.dir, __dirname, 'dir matches') + var app = budo('test/app.js', { + dir: __dirname, + port: 8000 + }) + .on('error', function(err) { + t.fail(err) + }) + .on('connect', function(ev) { + t.deepEqual(ev.entries, [ 'test/app.js' ], 'entries matches') + t.equal(ev.serve, 'bundle.js', 'mapping matches') + t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') + t.equal(ev.host, 'localhost', 'host is not specified') + t.equal(ev.port, 8000, 'port matches') + t.equal(ev.dir, __dirname, 'dir matches') + app.close() + }) + .on('reload', function() { + t.fail('should not have received reload event') + }) + .on('watch', function() { + t.fail('should not have received watch event') + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) -// app -// .once('update', function(type) { -// t.ok(true, 'got bundle.js update') -// app.close() -// cleanup() -// }) -// }) -// .on('reload', function() { -// t.fail('should not have received reload event') -// }) -// .on('exit', function() { -// t.ok(true, 'closing') -// }) -// }) +test('entry mapping', function(t) { + t.plan(2) + t.timeoutAfter(10000) + + var app = budo(['test/app:foo.js', 'test/other.js']) + .on('connect', function(ev) { + t.equal(ev.serve, 'foo.js', 'mapping matches') + t.deepEqual(ev.entries, ['test/app', 'test/other.js'], 'from matches') + app.close() + }) +}) -test('sets live() with args before connect', function(t) { - t.plan(4) +test('--serve allows explicit bundle renaming', function(t) { + t.plan(2) + t.timeoutAfter(10000) + + var app = budo(['test/app', 'test/other.js'], { serve: 'static/foo.js' }) + .on('connect', function(ev) { + t.equal(ev.serve, 'static/foo.js', 'mapping matches') + t.deepEqual(ev.entries, ['test/app', 'test/other.js'], 'from matches') + app.close() + }) +}) + +test('sets watch() and live() by default with live: true', function(t) { + t.plan(3) t.timeoutAfter(10000) var app = budo('test/app.js', { dir: __dirname, port: 8000, - outfile: 'bundle.js' + live: true }) - //manually enable LiveReload server - .live() - //manually trigger LiveReload event on bundle.js update - .on('update', function() { - t.ok('got live reload event') - app.reload() + .once('update', function() { + //bundle.js changed + t.ok(true, 'got update event') }) testLive(t, app) }) -test('sets watch() and live() by default with live: true', function(t) { +test('allow setting live() manually', function(t) { t.plan(3) t.timeoutAfter(10000) @@ -66,18 +83,20 @@ test('sets watch() and live() by default with live: true', function(t) { port: 8000, live: true }) + .live() //start live server + .on('update', function() { + t.ok(true, 'got first update') + app.reload() + }) testLive(t, app) }) function testLive(t, app) { app - .once('update', function() { //bundle.js changed - t.ok(true, 'got update event') - }) - .once('reload', function(err) { //LiveReload triggered + .once('reload', function(err) { + //LiveReload triggered t.ok(true, 'got reload event') app.close() - cleanup() }) .on('error', function(err) { t.fail(err) diff --git a/test/test-close-immediate.js b/test/test-close-immediate.js new file mode 100644 index 0000000..f992478 --- /dev/null +++ b/test/test-close-immediate.js @@ -0,0 +1,46 @@ +var test = require('tape') +var budo = require('../') +var path = require('path') + +test('can close on connect with watch/live', function(t) { + t.plan(2) + t.timeoutAfter(10000) + + var app = budo('test/app.js', { + dir: __dirname, + port: 8000, + outfile: 'bundle.js' + }) + .live() + .watch() + .on('connect', function() { + t.ok(true, 'connected') + app.close() + }) + .on('update', function() { + t.fail(true, 'got update') + }) + .on('exit', function() { + t.ok(true, 'got exit') + }) +}) + + +test('can close on first update', function(t) { + t.plan(3) + t.timeoutAfter(10000) + + var app = budo('test/app.js', { + dir: __dirname + }) + .on('update', function() { + t.ok(true, 'got update') + app.close() + }) + .once('connect', function() { + t.ok(true, 'connected') + }) + .on('exit', function() { + t.ok(true, 'got exit') + }) +}) diff --git a/test/test-close.js b/test/test-close.js deleted file mode 100644 index a90500e..0000000 --- a/test/test-close.js +++ /dev/null @@ -1,78 +0,0 @@ -var test = require('tape') -var budo = require('../') -var spawn = require('win-spawn') -var fs = require('fs') -var path = require('path') -var cleanup = require('./cleanup') -var ndjson = require('ndjson') -var kill = require('tree-kill') - -var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') - -test('should close budo API and delete tmp dirs', function(t) { - t.plan(3) - t.timeoutAfter(5000) - - var tmpFile - - var app = budo('test/app.js', { - dir: __dirname, - port: 8000, - }) - .on('connect', function(ev) { - tmpFile = ev.from - t.ok(true, 'connected') - setTimeout(function() { - app.close() - }, 1000) - }) - .on('exit', function() { - t.ok(true, 'exiting') - doesNotExist(t, tmpFile) - }) - .on('error', function(err) { - t.fail(err) - }) -}) - -test('should close budo CLI and delete tmp dirs', function(t) { - t.plan(2) - var timeout = 2000 - t.timeoutAfter(timeout) - var tmpDir - - var proc = spawn(cliPath, ['app.js'], { - cwd: __dirname, - env: process.env - }) - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - if (!data || !data.message) - return - - var msg = 'temp directory created at' - var idx = data.message.indexOf(msg) - - if (idx === -1) - return - - tmpDir = data.message.substring(idx).trim() - proc.on('exit', function() { - t.ok(true, 'got exit event') - doesNotExist(t, tmpDir) - }) - kill(proc.pid) - }) - proc.stderr.pipe(process.stderr) -}) - -function doesNotExist(t, file) { - setTimeout(function() { - fs.stat(file, function(err) { - if (err) - t.ok(true, 'tmpfile removed') - else - t.fail('tmpfile was not destroyed') - }) - }, 500) -} \ No newline at end of file diff --git a/test/test-file-watch.js b/test/test-file-watch.js new file mode 100644 index 0000000..594a983 --- /dev/null +++ b/test/test-file-watch.js @@ -0,0 +1,11 @@ +var test = require('tape') +var createFileWatch = require('../lib/file-watch') + +test('should close immediately', function(t) { + var watcher = createFileWatch('*.html') + watcher.on('watch', function() { + t.fail('should not get watch event') + }) + watcher.close() + t.end() +}) diff --git a/test/test-live.js b/test/test-live.js index 4b91a16..cc045ab 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -10,28 +10,25 @@ test('should inject LiveReload snippet', function(t) { t.plan(4) t.timeoutAfter(10000) - var server - var entry = path.join(__dirname, 'app.js') - budo(entry, { + var app = budo(entry, { dir: __dirname, port: 8000, - outfile: 'bundle.js', + serve: 'app.js', live: true }) .on('error', function(err) { t.fail(err) }) - .on('watch', function(event, file) { - t.equal(path.basename(file), 'bundle.js', 'watch event triggered') + .on('update', function() { + t.ok(true, 'update event triggered') }) .on('reload', function(file) { - t.equal(path.basename(file), 'bundle.js', 'reload event triggered') + t.equal(file, 'app.js', 'reload event triggered') cleanup() - server.close() + app.close() }) .on('connect', function(ev) { - server = ev matchesHTML(t, ev.uri) setTimeout(function() { fs.writeFile(entry, source) @@ -42,37 +39,30 @@ test('should inject LiveReload snippet', function(t) { }) }) - test('manual LiveReload triggering', function(t) { t.plan(4) t.timeoutAfter(10000) - var server - var entry = path.join(__dirname, 'app.js') var app = budo(entry, { dir: __dirname, port: 8000, - outfile: 'bundle.js', - live: false, - 'live-script': true + serve: 'app.js' }) .watch() .live() .on('error', function(err) { t.fail(err) }) - .on('watch', function(event, file) { - t.equal(path.basename(file), 'bundle.js', 'watch event triggered') + .on('update', function(file) { + t.equal(file, 'app.js', 'update event triggered') app.reload(file) }) .on('reload', function(file) { - t.equal(path.basename(file), 'bundle.js', 'reload event triggered') - cleanup() - server.close() + t.equal(file, 'app.js', 'reload event triggered') + app.close() }) .on('connect', function(ev) { - server = ev matchesHTML(t, ev.uri) setTimeout(function() { fs.writeFile(entry, source) @@ -83,13 +73,43 @@ test('manual LiveReload triggering', function(t) { }) }) -function matchesHTML(t, uri) { + +test('should not inject LiveReload snippet', function(t) { + t.plan(1) + t.timeoutAfter(10000) + + var entry = path.join(__dirname, 'app.js') + var app = budo(entry, { + dir: __dirname, + port: 8000, + serve: 'app.js', + }) + .live({ plugin: true }) + .on('error', function(err) { + t.fail(err) + }) + .on('connect', function(ev) { + // matchesHTML(t, ev.uri, getHTMLNoLive()) + // setTimeout(function() { + app.close() + // }, 100) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) +}) + +function matchesHTML(t, uri, html) { request.get({ uri: uri + 'index.html' }, function(err, resp, body) { if (err) t.fail(err) - t.equal(body, getHTML(), 'matches expected HTML') + t.equal(body, html || getHTML(), 'matches expected HTML') }) } +function getHTMLNoLive() { + return '' +} + function getHTML() { - return '' + return '' } \ No newline at end of file From e86b3683213e793c9511fce9840a89f112d6afd0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 22:49:25 -0400 Subject: [PATCH 059/302] linting, cleaner tests, code cleanup --- bin/cmd.js | 2 +- example/app.js | 2 +- example/other.js | 2 +- index.js | 11 +----- lib/budo.js | 25 ++++++------- lib/file-watch.js | 8 ++--- lib/get-output.js | 25 ------------- lib/server.js | 37 ++++++++++--------- lib/tinylr.js | 2 +- lib/tmpdir.js | 39 --------------------- lib/watchify.js | 14 +++----- package.json | 9 +++-- test/{test-file-watch.js => test-server.js} | 0 13 files changed, 51 insertions(+), 125 deletions(-) delete mode 100644 lib/get-output.js delete mode 100644 lib/tmpdir.js rename test/{test-file-watch.js => test-server.js} (100%) diff --git a/bin/cmd.js b/bin/cmd.js index e06949e..e1248c1 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -16,7 +16,7 @@ var showHelp = opts.h || opts.help if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { console.error('ERROR: no entry scripts specified\n use --help for examples') - return + process.exit(1) } if (showHelp) { diff --git a/example/app.js b/example/app.js index 4ef0974..b76ffc6 100644 --- a/example/app.js +++ b/example/app.js @@ -22,7 +22,7 @@ require('raf-loop')(function(dt) { ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 50, 100, 100) + ctx.fillRect(Math.sin(time)*50 + 300, 50, 20, 20) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) diff --git a/example/other.js b/example/other.js index 25044f0..49bb4af 100644 --- a/example/other.js +++ b/example/other.js @@ -1 +1 @@ -console.log("FOO") \ No newline at end of file +console.log("fas") \ No newline at end of file diff --git a/index.js b/index.js index bdd3902..682a791 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,10 @@ var bole = require('bole') var log = bole('budo') var xtend = require('xtend') -var assign = require('xtend/mutable') -var Emitter = require('events/') -var getOutput = require('./lib/get-output') -var rimraf = require('rimraf') -var path = require('path') - var budo = require('./lib/budo') module.exports = function(entry, opts) { - var argv = assign({}, opts) + var argv = xtend(opts) if (argv.stream) { bole.output({ @@ -28,7 +22,6 @@ module.exports = function(entry, opts) { return emitter } - //e.g. //clean up entries and take the first one for bundle mapping var file @@ -46,8 +39,6 @@ module.exports = function(entry, opts) { argv.port = typeof argv.port === 'number' ? argv.port : 9966 argv.dir = argv.dir || process.cwd() - - var map = mapping(entries[0]) argv.serve = file //run watchify server diff --git a/lib/budo.js b/lib/budo.js index 813830c..c50d76f 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -1,13 +1,13 @@ -var path = require('path') var Emitter = require('events/') var xtend = require('xtend') var assign = require('xtend/mutable') -var http = require('./server').http var log = require('bole')('budo') +var dargs = require('dargs') + +var createServer = require('./server') var createFileWatch = require('./file-watch') var createTinylr = require('./tinylr') var createWatchify = require('./watchify') -var dargs = require('dargs') var DEFAULT_DELAY = 0 @@ -29,7 +29,7 @@ module.exports = function() { emitter._start = function(entries, opt) { opt = opt || {} var port = opt.port - server = http(opt) + server = createServer(opt) .on('error', function(err) { emitter.emit('error', err) }) @@ -47,9 +47,12 @@ module.exports = function() { defaultGlobs = ['**/*.{html,css}'] //watchify@3 has some extra options + var ignoreWatchArg = typeof opt['ignore-watch'] === 'undefined' + ? opt.iw + : opt['ignore-watch'] defaultWatchOpts = { poll: opt.poll, - 'ignore-watch': typeof opt['ignore-watch'] === 'undefined' ? opt.iw : opt['ignore-watch'] + 'ignore-watch': ignoreWatchArg } //and default live options defaultLiveOpts = { @@ -110,10 +113,10 @@ module.exports = function() { liveOpt = liveOpt||{} server._live = !liveOpt.plugin tinylr = createTinylr(xtend(defaultLiveOpts, liveOpt)) - emitter.reload = function(path) { - path = path - tinylr.reload(path) - emitter.emit('reload', path) + emitter.reload = function(file) { + file = file + tinylr.reload(file) + emitter.emit('reload', file) } emitter.live = noop } @@ -123,7 +126,7 @@ module.exports = function() { //close everything emitter.close = function() { if (closed) - return + return emitter closed = true if (watchify) watchify.close() @@ -139,7 +142,6 @@ module.exports = function() { return emitter } - return emitter function runWatchify(entries, opt) { @@ -168,7 +170,6 @@ module.exports = function() { .on('error', emitter.emit.bind(emitter, 'error')) } - function noop() { return emitter } diff --git a/lib/file-watch.js b/lib/file-watch.js index 82001d5..5b9612e 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -19,9 +19,9 @@ module.exports = function(glob, opt) { var emitter = new Emitter() var closed = false, ready = false - watcher = watch(glob, opt) - watcher.on('add', reload.bind(null, 'add')) - watcher.on('change', reload.bind(null, 'change')) + var watcher = watch(glob, opt) + watcher.on('add', onWatch.bind(null, 'add')) + watcher.on('change', onWatch.bind(null, 'change')) // chokidar@1.0.0-r6 only allows close after ready event watcher.once('ready', function() { @@ -30,7 +30,7 @@ module.exports = function(glob, opt) { watcher.close() }) - function reload(event, path) { + function onWatch(event, path) { emitter.emit('watch', event, path) log.debug({ type: event, diff --git a/lib/get-output.js b/lib/get-output.js deleted file mode 100644 index 64174bc..0000000 --- a/lib/get-output.js +++ /dev/null @@ -1,25 +0,0 @@ -var path = require('path') -var tmpdir = require('./tmpdir') -var quote = require('shell-quote') - -//get an output directory, from user or tmp dir -module.exports = function getOutput(argv, cb) { - var outfile = argv.o || argv.outfile - var from - - //outpipe was added in watchify@3.1.0 - if (outfile.indexOf('|') >= 0 || outfile.indexOf('>') >= 0) { - var err = new Error("unsupported outpipe command") - err.name = 'OUTPIPE' - return cb(err) - } else { - from = path.join(argv.dir, outfile) - } - - return { - from: from, - to: outfile, - outpipe: outpipe, - dir: argv.dir - } -} \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 066d145..f6b97ae 100644 --- a/lib/server.js +++ b/lib/server.js @@ -8,9 +8,8 @@ var fs = require('fs') var html = require('./default-index') var inject = require('inject-lr-script') var Emitter = require('events/') -var noop = function(){} -module.exports.http = function(opts) { +module.exports = function(opts) { var handler = createHandler(opts) var server = http.createServer(handler.router) @@ -52,45 +51,49 @@ function createHandler(opts) { port: opts['live-port'] } - var entryHandler = staticHandler - - router.addRoute('/' + opts.serve, function(req, res, params) { + router.addRoute('/' + opts.serve, function(req, res) { log.info({ url: req.url, type: 'static' }) if (emitter.pending) { - console.log("Bundle pending...") + log.debug("bundle pending") emitter.once('update', function() { - console.log("Bundle updated...") + log.debug("bundle ready") submit(req, res) }) } else { - console.log("Bundle ready...") submit(req, res) } }) router.addRoute('/index.html', home) router.addRoute('/', home) - - router.addRoute('*', function(req, res, params) { - log.info({ - url: req.url, - type: 'static' - }) - staticHandler(req, res) - }) + router.addRoute('*.html', wildcard(true)) + router.addRoute('*', wildcard()) return emitter + function wildcard(html) { + return function(req, res) { + //inject LiveReload into HTML content if needed + if (html && emitter.live) + res = inject(res, liveOpts) + log.info({ + url: req.url, + type: 'static' + }) + staticHandler(req, res) + } + } + function submit(req, res) { res.setHeader('content-type', 'text/javascript') res.end(emitter.contents) } - function home(req, res, params) { + function home(req, res) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed if (emitter.live) diff --git a/lib/tinylr.js b/lib/tinylr.js index ae5f7ad..21c268a 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -12,7 +12,7 @@ module.exports = function(opt) { var server = tinylr() var closed = false, ready = false - server.listen(opt.port, opt.host, function(a) { + server.listen(opt.port, opt.host, function() { ready = true if (closed) return server.close() diff --git a/lib/tmpdir.js b/lib/tmpdir.js deleted file mode 100644 index 46c3544..0000000 --- a/lib/tmpdir.js +++ /dev/null @@ -1,39 +0,0 @@ -var rimraf = require('rimraf') -var log = require('bole')('budo') -var tmp = require('tmp') - -tmp.setGracefulCleanup() - -module.exports = function(cb) { - tmp.dir({ - mode: '0755', - prefix: 'budo-' - }, - function(err, filepath) { - if (!err) { - process.on('exit', remove) - process.on('SIGINT', exit) - process.on('uncaughtException', exit) - log.debug('temp directory created at', filepath) - } - - cb(err, filepath) - - function remove(err) { - try { - rimraf.sync(filepath) - } catch (e) { - rimraf.sync(filepath) - } - if (err) - console.error(err.stack) - } - - function exit(err) { - if (err) - console.error(err.stack) - process.exit() - } - }) - -} \ No newline at end of file diff --git a/lib/watchify.js b/lib/watchify.js index da42cdf..da33679 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,10 +1,7 @@ var log = require('bole')('budo') -var path = require('path') var getWatchify = require('./get-watchify') -var dargs = require('dargs') var Emitter = require('events/') var debounce = require('debounce') -var fs = require('fs') var path = require('path') var concat = require('concat-stream') @@ -16,21 +13,19 @@ module.exports = function(watchifyArgs, opt) { var watchify var time = Date.now() - var first = true var contents = null emitter.close = function() { if (closed) return closed = true - emitter.removeAllListeners('update') if (watchify) { //needed for watchify@3.0.0 //see test-close-immediate //this needs to be revisited upstream setTimeout(function() { - watchify.close() - }, 100) + watchify.close() + }, 50) } } @@ -41,7 +36,8 @@ module.exports = function(watchifyArgs, opt) { 'Example:', ' npm install watchify --save-dev\n' ].join('\n') - return emitter.emit('error', new Error(msg)) + emitter.emit('error', new Error(msg)) + return } if (closed) return @@ -65,7 +61,7 @@ module.exports = function(watchifyArgs, opt) { function bundle() { if (closed) { - process.nextTick(update) + update() return } diff --git a/package.json b/package.json index 7332f9c..56189c3 100644 --- a/package.json +++ b/package.json @@ -60,11 +60,10 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/app.js --dir example --verbose | garnish", - "live": "./bin/cmd.js example/app.js --dir example --live | garnish", - "uglify": "./bin/cmd.js example/app.js -v --dir example --live -o 'uglifyjs > bundle.js' | garnish -v", - "live-plugin": "./bin/cmd.js example/app.js --dir example --live-plugin | garnish", - "remap": "./bin/cmd.js example/app:bundle2.js --dir example --live | garnish" + "start": "./bin/cmd.js example/*.js --dir example --verbose | garnish", + "live": "./bin/cmd.js example/*.js --dir example --live -v | garnish -v", + "live-plugin": "./bin/cmd.js example/*.js --dir example --live-plugin -v | garnish", + "remap": "./bin/cmd.js example/*.js --serve bundle2.js --dir example --live -v | garnish" }, "keywords": [ "browserify", diff --git a/test/test-file-watch.js b/test/test-server.js similarity index 100% rename from test/test-file-watch.js rename to test/test-server.js From 7c0ebeb6df83c0bad34a7de1457bf5535e36be0d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 23:23:46 -0400 Subject: [PATCH 060/302] udpating readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5f26729..ac800ae 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +### version 3.0 roadmap + +[See here](https://github.com/mattdesl/budo/issues/20#issuecomment-89889926) the budo@3.0 plans. The next version will be snappier and will never serve you stale or empty bundles (i.e. reloading page mid-compilation), but comes with some breaking changes. + # budō [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) From 37a1fc32fefe4d328e0346b02b9a02dd56face99 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 5 Apr 2015 23:23:50 -0400 Subject: [PATCH 061/302] 2.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 579b8c9..0bfb203 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.3", + "version": "2.1.4", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From e7e2ce2004794b7ec731c07c14f5a2ce3415f62a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 6 Apr 2015 11:25:08 -0400 Subject: [PATCH 062/302] preparation for deprecation; remove outfile from suggested docs --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index ac800ae..98d9a19 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,6 @@ The simplest use cases will start up a server with a default `index.html` and in #run watchify on port 9966 budo index.js -#run watchify with explicit output file -budo index.js --outfile bundle.js --verbose - #run watchify with some options and trigger LiveReload on change budo index.js --live --transform brfs ``` From 99ce567284275a617b40dc8c01e19d1eb5f72bbf Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 6 Apr 2015 11:25:11 -0400 Subject: [PATCH 063/302] 2.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0bfb203..defdd8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.4", + "version": "2.1.5", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 59a48335a11c927061254584d3687170f8ce8567 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 7 Apr 2015 16:41:59 -0400 Subject: [PATCH 064/302] pass through contents --- lib/budo.js | 2 +- lib/watchify.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index c50d76f..c2d6cab 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -165,7 +165,7 @@ module.exports = function() { .on('update', function(contents) { if (server) server.update(contents) - emitter.emit('update', serveAs) + emitter.emit('update', serveAs, contents) }) .on('error', emitter.emit.bind(emitter, 'error')) } diff --git a/lib/watchify.js b/lib/watchify.js index da33679..864a790 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -90,7 +90,7 @@ module.exports = function(watchifyArgs, opt) { if (opt.verbose && !didError) { var delay = Date.now() - time log.info({ - elapsed: (delay / 1000).toFixed(2) + ' s', + elapsed: Math.round(delay) + 'ms', type: 'bundle', url: opt.serve }) From e85cc312c8a5b44fff37043caa19de7167f009d3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 7 Apr 2015 20:50:50 -0400 Subject: [PATCH 065/302] cleaning up deps, notifying error for now on outfile --- README.md | 8 ++++---- bin/cmd.js | 7 ++++++- index.js | 7 +++---- package.json | 8 -------- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ce6b026..5997a0c 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ The simplest use cases will start up a server with a default `index.html` and in #run watchify on port 9966 budo index.js -#run watchify with explicit output file +#run watchify with timing information budo index.js --verbose -#run watchify with some options and trigger LiveReload on file change +#run watchify with some options and trigger LiveReload on JS/HTML/CSS file change budo index.js --live --transform brfs ``` @@ -28,12 +28,12 @@ You can open `localhost:9966` to see the content in action. To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used. ```sh -budo index.js -v | garnish +budo index.js | garnish ``` See [docs](#docs) for more features. -PRs/suggestions/comments welcome. Props to [@caspervonb](https://twitter.com/caspervonb) for the early groundwork. +PRs/suggestions/comments welcome. ## docs diff --git a/bin/cmd.js b/bin/cmd.js index e1248c1..eebe286 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -15,7 +15,7 @@ delete opts._ var showHelp = opts.h || opts.help if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { - console.error('ERROR: no entry scripts specified\n use --help for examples') + console.error('ERROR:\n no entry scripts specified\n use --help for examples') process.exit(1) } @@ -28,6 +28,11 @@ if (showHelp) { return } +if (opts.o || opts.outfile) { + console.error('ERROR:\n --outfile has been removed in budo@3.0') + return +} + var basePort = opts.port || opts.p || 9966 getport(basePort, function(err, port) { if (err) { diff --git a/index.js b/index.js index 682a791..c4c518c 100644 --- a/index.js +++ b/index.js @@ -22,7 +22,6 @@ module.exports = function(entry, opts) { return emitter } - //e.g. //clean up entries and take the first one for bundle mapping var file entries = entries.map(function(entry, i) { @@ -33,9 +32,9 @@ module.exports = function(entry, opts) { }) //if user specified -o use that as our entry map - var outfile = argv.serve - if (outfile && typeof outfile === 'string') - file = outfile + var serveAs = argv.serve + if (serveAs && typeof serveAs === 'string') + file = serveAs argv.port = typeof argv.port === 'number' ? argv.port : 9966 argv.dir = argv.dir || process.cwd() diff --git a/package.json b/package.json index 56189c3..63209e0 100644 --- a/package.json +++ b/package.json @@ -20,22 +20,14 @@ "debounce": "^1.0.0", "ecstatic": "^0.5.8", "events": "^1.0.2", - "find-global-packages": "0.0.1", - "find-parent-dir": "^0.3.0", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "minimist": "^1.1.0", - "npm-execspawn": "^1.0.6", - "npm-which": "^2.0.0", "resolve": "^1.1.6", "resolve-npm-which": "^1.0.1", - "response-stream": "0.0.0", - "rimraf": "^2.2.8", "routes-router": "^4.1.2", - "shell-quote": "^1.4.3", "through2": "^0.6.3", "tiny-lr": "^0.1.5", - "tmp": "0.0.24", "xtend": "^4.0.0" }, "devDependencies": { From 5309e18f904b05b1a5a851575d45aa089978162e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 7 Apr 2015 20:51:26 -0400 Subject: [PATCH 066/302] release candidate 1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63209e0..1622b77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "2.1.3", + "version": "3.0.0-rc1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 06fab226a2930172370a1681bd1e5d24600c84d4 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 7 Apr 2015 20:52:51 -0400 Subject: [PATCH 067/302] fix test dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1622b77..b39bf75 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "npm-execspawn": "^1.0.6", "raf-loop": "^1.0.1", "request": "^2.53.0", + "rimraf": "^2.3.2", "tap-finished": "0.0.1", "tap-spec": "^2.2.1", "tape": "^3.5.0", From 78b0f6b3cc470d9d735ce3b3ec74a06fac7da93f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 8 Apr 2015 15:19:40 -0400 Subject: [PATCH 068/302] don't allow port to collide with plugin opt for browserify/watchify --- bin/cmd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cmd.js b/bin/cmd.js index eebe286..7749dfd 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -33,7 +33,7 @@ if (opts.o || opts.outfile) { return } -var basePort = opts.port || opts.p || 9966 +var basePort = opts.port || 9966 getport(basePort, function(err, port) { if (err) { console.error("Could not find available port", err) From 0a001ba98bc78bea681b9eca72a1057e48505cd0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 8 Apr 2015 15:19:59 -0400 Subject: [PATCH 069/302] bail if dir is not a string --- index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index c4c518c..20298ec 100644 --- a/index.js +++ b/index.js @@ -18,8 +18,7 @@ module.exports = function(entry, opts) { var entries = Array.isArray(entry) ? entry : [entry] entries = entries.filter(Boolean) if (entries.length === 0) { - bail("No entry scripts specified!") - return emitter + return bail("No entry scripts specified!") } //clean up entries and take the first one for bundle mapping @@ -40,6 +39,9 @@ module.exports = function(entry, opts) { argv.dir = argv.dir || process.cwd() argv.serve = file + if (typeof argv.dir !== 'string') + return bail('--dir must be a path') + //run watchify server emitter.on('connect', setupLive) emitter._start(entries, argv) @@ -79,5 +81,6 @@ module.exports = function(entry, opts) { process.nextTick(function() { emitter.emit('error', new Error(msg)) }) + return emitter } } \ No newline at end of file From 5cc62a04f3e756fce24906871ae92ed4fc0d2383 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 10:32:40 -0400 Subject: [PATCH 070/302] don't pass serve opt to watchify --- lib/budo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/budo.js b/lib/budo.js index c2d6cab..7bc4aa5 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -220,6 +220,7 @@ function getWatchifyArgs(entries, opt) { delete opt.port delete opt.host delete opt.live + delete opt.serve delete opt['live-port'] delete opt['live-script'] delete opt['live-plugin'] From 50fae69f762cbb5890c45d6fad294a0913c06118 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 12:00:03 -0400 Subject: [PATCH 071/302] test cleanup; serve entry basename by default; fix up help; upgrade dependencies --- README.md | 22 ++--- bin/help.txt | 24 ++--- index.js | 3 +- lib/watchify.js | 2 +- package.json | 18 ++-- test/app.js | 3 - test/cleanup.js | 17 ---- test/fixtures/app-brfs.js | 2 + test/fixtures/app.js | 1 + test/fixtures/first.js | 1 + test/fixtures/second.js | 2 + test/fixtures/text.txt | 1 + test/test-api.js | 20 ++--- test/test-bundle.js | 90 +++++++++++++++++++ test/test-cli.js | 166 ----------------------------------- test/test-close-immediate.js | 4 +- test/test-live.js | 10 +-- test/test-server.js | 11 --- 18 files changed, 151 insertions(+), 246 deletions(-) delete mode 100644 test/app.js delete mode 100644 test/cleanup.js create mode 100644 test/fixtures/app-brfs.js create mode 100644 test/fixtures/app.js create mode 100644 test/fixtures/first.js create mode 100644 test/fixtures/second.js create mode 100644 test/fixtures/text.txt create mode 100644 test/test-bundle.js delete mode 100644 test/test-cli.js delete mode 100644 test/test-server.js diff --git a/README.md b/README.md index 5997a0c..eafd284 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ budo index.js budo index.js --verbose #run watchify with some options and trigger LiveReload on JS/HTML/CSS file change -budo index.js --live --transform brfs +budo index.js --live --transform babelify ``` You can open `localhost:9966` to see the content in action. -To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used. +To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used: ```sh budo index.js | garnish @@ -55,16 +55,18 @@ Details for `budo` command-line interface. Other options like `--verbose` and `- ```sh Usage: - budo [entries] [opts] + budo [entries] [opts] Options: - --help, -h show help message - --port the port to run, default 9966 - --host the host, default "localhost" - --dir the directory to serve, and the base for --outfile - --live enable LiveReload integration with a script tag - --live-plugin enable LiveReload for use with a browser plugin - --live-port the LiveReload port, default 35729 + --help, -h show help message + --port the port to run, default 9966 + --host the host, default "localhost" + --dir the directory to serve, and the base for --outfile + --live enable LiveReload integration + --live-plugin enable LiveReload but do not inject script tag + --live-port the LiveReload port, default 35729 + --verbose, -v verbose timing information for re-bundles + --poll=N use polling for file watch, with optional interval N ``` By default, the `--debug` option will be sent to watchify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. diff --git a/bin/help.txt b/bin/help.txt index e2404fe..e5ac842 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -2,16 +2,20 @@ Usage: budo index.js [opts] [browserify opts] Options: - --help, -h show help message - --outfile, -o path to output bundle - --port the port to run, default 9966 - --host the host, default "localhost" - --dir the directory to serve, and the base for --outfile - --live enable LiveReload integration - --live-plugin enable LiveReload but do not inject script tag - --live-port the LiveReload port, default 35729 + --help, -h show help message + --port the port to run, default 9966 + --host the host, default "localhost" + --dir the directory to serve, and the base for --outfile + --live enable LiveReload integration + --live-plugin enable LiveReload but do not inject script tag + --live-port the LiveReload port, default 35729 + --verbose, -v verbose timing information for re-bundles + --poll=N use polling for file watch, with optional interval N + +Other Options: +https://github.com/substack/node-browserify Examples: - budo index.js --live --dir app/ --outfile bundle.js + budo src/index.js --live --dir app budo index.js --verbose --transform brfs - budo index.js:test.js --port 3000 + budo index.js:bundle.js --port 8000 diff --git a/index.js b/index.js index 20298ec..7c78b2d 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var bole = require('bole') var log = bole('budo') var xtend = require('xtend') var budo = require('./lib/budo') +var path = require('path') module.exports = function(entry, opts) { var argv = xtend(opts) @@ -74,7 +75,7 @@ module.exports = function(entry, opts) { if (parts.length > 1 && parts[1].length > 0) { return { from: parts[0], to: parts[1] } } - return { from: entry, to: 'bundle.js' } + return { from: entry, to: path.basename(entry) } } function bail(msg) { diff --git a/lib/watchify.js b/lib/watchify.js index 864a790..31db1c7 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -2,9 +2,9 @@ var log = require('bole')('budo') var getWatchify = require('./get-watchify') var Emitter = require('events/') var debounce = require('debounce') -var path = require('path') var concat = require('concat-stream') +//Eventually this may split into a watchify-server module module.exports = function(watchifyArgs, opt) { var emitter = new Emitter() var delay = opt.delay diff --git a/package.json b/package.json index b39bf75..bf304d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "3.0.0-rc1", + "version": "3.0.1-rc1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { @@ -14,11 +14,11 @@ }, "dependencies": { "bole": "^2.0.0", - "chokidar": "^1.0.0-rc5", + "chokidar": "^1.0.1", "concat-stream": "^1.4.8", "dargs": "^4.0.0", "debounce": "^1.0.0", - "ecstatic": "^0.5.8", + "ecstatic": "^0.7.2", "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", @@ -32,11 +32,12 @@ }, "devDependencies": { "2d-context": "^1.2.0", - "browserify": "^8.1.3", + "brfs": "^1.4.0", + "browserify": "^9.0.8", "canvas-fit": "^1.2.0", "concat-stream": "^1.4.7", "domready": "^1.0.7", - "garnish": "^2.0.1", + "garnish": "^2.1.2", "inject-lr-script": "^1.0.1", "ndjson": "^1.3.0", "npm-execspawn": "^1.0.6", @@ -44,11 +45,12 @@ "request": "^2.53.0", "rimraf": "^2.3.2", "tap-finished": "0.0.1", - "tap-spec": "^2.2.1", - "tape": "^3.5.0", + "tap-spec": "^3.0.0", + "tape": "^4.0.0", "tree-kill": "0.0.6", "uglify-js": "^2.4.19", - "watchify": "^3.0.1", + "vm": "0.0.1", + "watchify": "^3.1.0", "win-spawn": "^2.0.0" }, "scripts": { diff --git a/test/app.js b/test/app.js deleted file mode 100644 index cfaeb47..0000000 --- a/test/app.js +++ /dev/null @@ -1,3 +0,0 @@ -process.nextTick(function() { - console.log(__dirname) -}) \ No newline at end of file diff --git a/test/cleanup.js b/test/cleanup.js deleted file mode 100644 index c4387dc..0000000 --- a/test/cleanup.js +++ /dev/null @@ -1,17 +0,0 @@ -var rimraf = require('rimraf') -var path = require('path') - -module.exports = function cleanup(paths) { - setTimeout(function() { - var defaults = [ - path.join(__dirname, '/bundle.js'), - path.join(__dirname, '/.bundle.js') - ] - paths = paths || defaults - paths.forEach(function(file) { - rimraf(file, function(err) { - if (err) console.error(err) - }) - }) - }, 50) -} \ No newline at end of file diff --git a/test/fixtures/app-brfs.js b/test/fixtures/app-brfs.js new file mode 100644 index 0000000..14f6f19 --- /dev/null +++ b/test/fixtures/app-brfs.js @@ -0,0 +1,2 @@ +var text = require('fs').readFileSync(__dirname+'/text.txt', 'utf8') +console.log(text) \ No newline at end of file diff --git a/test/fixtures/app.js b/test/fixtures/app.js new file mode 100644 index 0000000..8bf4969 --- /dev/null +++ b/test/fixtures/app.js @@ -0,0 +1 @@ +console.log('from browserify') \ No newline at end of file diff --git a/test/fixtures/first.js b/test/fixtures/first.js new file mode 100644 index 0000000..991eba2 --- /dev/null +++ b/test/fixtures/first.js @@ -0,0 +1 @@ +global.start = 'foo' \ No newline at end of file diff --git a/test/fixtures/second.js b/test/fixtures/second.js new file mode 100644 index 0000000..056c32a --- /dev/null +++ b/test/fixtures/second.js @@ -0,0 +1,2 @@ +global.end = 'bar' +console.log(global.start+' '+global.end) \ No newline at end of file diff --git a/test/fixtures/text.txt b/test/fixtures/text.txt new file mode 100644 index 0000000..f6ea049 --- /dev/null +++ b/test/fixtures/text.txt @@ -0,0 +1 @@ +foobar \ No newline at end of file diff --git a/test/test-api.js b/test/test-api.js index 1132156..4da8f6a 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -1,13 +1,11 @@ var test = require('tape') var budo = require('../') -var cleanup = require('./cleanup') -var path = require('path') test('gets connect info', function(t) { t.plan(7) t.timeoutAfter(10000) - var app = budo('test/app.js', { + var app = budo('test/fixtures/app.js', { dir: __dirname, port: 8000 }) @@ -15,8 +13,8 @@ test('gets connect info', function(t) { t.fail(err) }) .on('connect', function(ev) { - t.deepEqual(ev.entries, [ 'test/app.js' ], 'entries matches') - t.equal(ev.serve, 'bundle.js', 'mapping matches') + t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') + t.equal(ev.serve, 'app.js', 'mapping matches') t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') t.equal(ev.host, 'localhost', 'host is not specified') t.equal(ev.port, 8000, 'port matches') @@ -38,10 +36,10 @@ test('entry mapping', function(t) { t.plan(2) t.timeoutAfter(10000) - var app = budo(['test/app:foo.js', 'test/other.js']) + var app = budo(['test/fixtures/app:foo.js', 'test/other.js']) .on('connect', function(ev) { t.equal(ev.serve, 'foo.js', 'mapping matches') - t.deepEqual(ev.entries, ['test/app', 'test/other.js'], 'from matches') + t.deepEqual(ev.entries, ['test/fixtures/app', 'test/other.js'], 'from matches') app.close() }) }) @@ -50,10 +48,10 @@ test('--serve allows explicit bundle renaming', function(t) { t.plan(2) t.timeoutAfter(10000) - var app = budo(['test/app', 'test/other.js'], { serve: 'static/foo.js' }) + var app = budo(['test/fixtures/app', 'test/other.js'], { serve: 'static/foo.js' }) .on('connect', function(ev) { t.equal(ev.serve, 'static/foo.js', 'mapping matches') - t.deepEqual(ev.entries, ['test/app', 'test/other.js'], 'from matches') + t.deepEqual(ev.entries, ['test/fixtures/app', 'test/other.js'], 'from matches') app.close() }) }) @@ -62,7 +60,7 @@ test('sets watch() and live() by default with live: true', function(t) { t.plan(3) t.timeoutAfter(10000) - var app = budo('test/app.js', { + var app = budo('test/fixtures/app.js', { dir: __dirname, port: 8000, live: true @@ -78,7 +76,7 @@ test('allow setting live() manually', function(t) { t.plan(3) t.timeoutAfter(10000) - var app = budo('test/app.js', { + var app = budo('test/fixtures/app.js', { dir: __dirname, port: 8000, live: true diff --git a/test/test-bundle.js b/test/test-bundle.js new file mode 100644 index 0000000..a8ba11a --- /dev/null +++ b/test/test-bundle.js @@ -0,0 +1,90 @@ +var test = require('tape') +var budo = require('../') + +var request = require('request') +var xtend = require('xtend') +var browserify = require('browserify') +var watchifyArgs = require('watchify').args +var path = require('path') +var vm = require('vm') + +test('serves app.js', run('test/fixtures/app.js')) +test('entry mapping to bundle.js', run('test/fixtures/app.js:bundle.js')) +test('turns off debug', run('test/fixtures/app', { debug: false })) +test('brfs transform', run('test/fixtures/app-brfs', { message: 'foobar', transform: 'brfs' })) +test('bundles multiple', run([ + 'test/fixtures/first', + 'test/fixtures/second' +], { + message: 'foo bar' +})) +test('bundles multiple and serves as static/bundle.js', run([ + 'test/fixtures/first', + 'test/fixtures/second' +], { + message: 'foo bar', + serve: 'static/bundle.js', + shouldServe: 'static/bundle.js' +})) + +function run(entries, opt) { + return function(t) { + matches(t, entries, opt) + } +} + +function matches(t, entries, opt) { + opt = xtend({ dir: __dirname, debug: true }, opt) + + var message = opt.message || 'from browserify' + var shouldServe = opt.shouldServe + t.plan(shouldServe ? 5 : 4) + var uri + if (!Array.isArray(entries)) + entries = [ entries ] + + + var app = budo(entries, opt) + .on('connect', function(ev) { + if (shouldServe) + t.equal(ev.serve, shouldServe, 'serves correct bundle file') + uri = ev.uri + ev.serve + t.ok(true, 'connected') + }) + .once('update', function() { + var b = browserify(xtend(watchifyArgs, { + debug: opt.debug, + }, opt)) + entries.forEach(function(entry) { + entry = entry.split(':')[0] + b.add(path.resolve(entry)) + }) + + b.bundle(function(err, expected) { + if (err) t.fail(err) + request.get({ + uri: uri + }, function(err, resp, data) { + if (err) t.fail(err) + + //make sure what browserify bundles matches what budo bundles + t.equal(data.toString(), expected.toString(), 'bundles match') + + //also compare output of running both bundles + vm.runInNewContext(expected, { + console: { log: log }, + global: {} + }); + + app.close() + }) + }) + }) + .on('exit', function() { + t.ok(true, 'closing') + }) + + function log(msg) { + t.equal(msg, message, 'the output matches in both cases') + } +} \ No newline at end of file diff --git a/test/test-cli.js b/test/test-cli.js deleted file mode 100644 index 315c0fd..0000000 --- a/test/test-cli.js +++ /dev/null @@ -1,166 +0,0 @@ -var rimraf = require('rimraf') -var fs = require('fs') -var path = require('path') -var spawn = require('win-spawn') -var npmSpawn = require('npm-execspawn') -var kill = require('tree-kill') - -var ndjson = require('ndjson') -var test = require('tape') -var concat = require('concat-stream') -var request = require('request') - -var cliPath = path.resolve(__dirname, '..', 'bin', 'cmd.js') - -test('should fail without scripts', function(t) { - t.plan(1) - var proc = spawn(cliPath) - proc.stderr.pipe(concat(function(str) { - var msg = str.toString().toLowerCase() - t.notEqual(msg.indexOf('no entry scripts specified'), -1) - })) -}) - -test('should run on available port', function(t) { - t.plan(1) - var proc = spawn(cliPath, ['app.js', '-o', 'bundle.js'], { - cwd: __dirname, - env: process.env - }) - var expected = 'Server running at' - - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - var msg = (data && data.message) || '' - t.ok(msg.indexOf(expected) >= 0, 'starts server') - kill(proc.pid) - }) - .on('error', function(err) { - t.fail(err) - kill(proc.pid) - }) -}) - -test('should get a bundle.js', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], - budo: ['app.js'], - }) -}) - -test('entry mapping to bundle2.js', function(t) { - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], - budo: ['app.js:bundle.js', '--no-debug'], - to: 'bundle.js' - }) -}) - -test('should get a bundle.js with --outfile', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js', '-d'], - budo: ['app.js', '-o', 'bundle.js'] - }) -}) - -test('should disable source maps with --no-debug', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - watchify: ['app.js', '-v', '-o', 'bundle-expected.js'], - budo: ['app.js', '-o', 'bundle.js', '--no-debug'] - }) -}) - -test('should get a bundle.js with --dir', function(t) { - var cwd = path.resolve(__dirname, '..') - runBundleMatch(t, { - cwd: cwd, - watchify: ['test/app.js', '-v', '-o', 'test/bundle-expected.js', '-d'], - budo: ['test/app.js', '-o', 'bundle.js', '--dir', 'test'] - }) -}) - -function runBundleMatch(t, opt) { - opt = opt || {} - - t.plan(1) - t.timeoutAfter(10000) - var cwd = opt.cwd || __dirname - var foundMsg = false - var bundle = path.join(__dirname, 'bundle.js') - var outputFile = 'bundle-expected.js' - var bundleExpected = path.join(__dirname, outputFile) - - //the expected bundle - var watchifyProc = npmSpawn('watchify ' + opt.watchify.join(' '), { - cwd: cwd, - env: process.env - }) - watchifyProc.stderr.on('data', watchifyDone) - watchifyProc.stdout.on('data', watchifyDone) - - function watchifyDone(msg) { - setTimeout(function() { - //timeout needed because of watchify@3.1.0 w/ outpipe - readWatchify(msg) - }, 50) - } - - function readWatchify(msg) { - var suc = msg.toString().indexOf(outputFile) - if (suc === -1) - t.fail('watchify process gave unexpected stdout/stderr message') - kill(watchifyProc.pid) - - fs.readFile(bundleExpected, 'utf8', function(err, data) { - if (err) - t.fail(err) - budoMatches(data) - }) - } - - function budoMatches(source) { - var proc = spawn(cliPath, opt.budo, { - cwd: cwd, - env: process.env - }) - proc.on('exit', cleanup) - proc.stderr.pipe(process.stderr) - proc.stdout.pipe(ndjson.parse()) - .on('data', function(data) { - var msg = (data.message || '').toLowerCase() - if (msg.indexOf('temp directory created') >= 0) - return - - var running = 'server running at ' - var idx = msg.indexOf(running) - if (idx >= 0) { - foundMsg = true - setTimeout(function() { //let bundling finish - var serverUrl = msg.substring(idx + running.length) - request.get({ - uri: serverUrl + (opt.to || 'bundle.js') - }, function(err, resp, data2) { - if (err) t.fail(err) - t.equal(data2, source, 'bundle matches') - kill(proc.pid) - }) - }, 1000) - } else if (!foundMsg) { - t.fail('no server running message in ' + msg) - kill(proc.pid) - } - }) - } - - function cleanup() { - rimraf(bundleExpected, function(err) { - if (err) console.error(err) - }) - rimraf(bundle, function(err) { - if (err) console.error(err) - }) - } -} \ No newline at end of file diff --git a/test/test-close-immediate.js b/test/test-close-immediate.js index f992478..22455e4 100644 --- a/test/test-close-immediate.js +++ b/test/test-close-immediate.js @@ -6,7 +6,7 @@ test('can close on connect with watch/live', function(t) { t.plan(2) t.timeoutAfter(10000) - var app = budo('test/app.js', { + var app = budo('test/fixtures/app.js', { dir: __dirname, port: 8000, outfile: 'bundle.js' @@ -30,7 +30,7 @@ test('can close on first update', function(t) { t.plan(3) t.timeoutAfter(10000) - var app = budo('test/app.js', { + var app = budo('test/fixtures/app.js', { dir: __dirname }) .on('update', function() { diff --git a/test/test-live.js b/test/test-live.js index cc045ab..eecb9c8 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -2,15 +2,14 @@ var test = require('tape') var budo = require('../') var path = require('path') var request = require('request') -var cleanup = require('./cleanup') var fs = require('fs') -var source = fs.readFileSync(path.join(__dirname, 'app.js'), 'utf8') +var source = fs.readFileSync(path.join(__dirname, 'fixtures', 'app.js'), 'utf8') test('should inject LiveReload snippet', function(t) { t.plan(4) t.timeoutAfter(10000) - var entry = path.join(__dirname, 'app.js') + var entry = path.join(__dirname, 'fixtures', 'app.js') var app = budo(entry, { dir: __dirname, port: 8000, @@ -25,7 +24,6 @@ test('should inject LiveReload snippet', function(t) { }) .on('reload', function(file) { t.equal(file, 'app.js', 'reload event triggered') - cleanup() app.close() }) .on('connect', function(ev) { @@ -43,7 +41,7 @@ test('manual LiveReload triggering', function(t) { t.plan(4) t.timeoutAfter(10000) - var entry = path.join(__dirname, 'app.js') + var entry = path.join(__dirname, 'fixtures', 'app.js') var app = budo(entry, { dir: __dirname, port: 8000, @@ -78,7 +76,7 @@ test('should not inject LiveReload snippet', function(t) { t.plan(1) t.timeoutAfter(10000) - var entry = path.join(__dirname, 'app.js') + var entry = path.join(__dirname, 'fixtures', 'app.js') var app = budo(entry, { dir: __dirname, port: 8000, diff --git a/test/test-server.js b/test/test-server.js deleted file mode 100644 index 594a983..0000000 --- a/test/test-server.js +++ /dev/null @@ -1,11 +0,0 @@ -var test = require('tape') -var createFileWatch = require('../lib/file-watch') - -test('should close immediately', function(t) { - var watcher = createFileWatch('*.html') - watcher.on('watch', function() { - t.fail('should not get watch event') - }) - watcher.close() - t.end() -}) From 3bc2541229f35d3baba9ee9601bf18678c7e8a90 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 13:13:56 -0400 Subject: [PATCH 072/302] fixing spaces and query params; using entry point for default serve result; adds query param to index.html but serves the pathname --- index.js | 8 +++---- lib/budo.js | 1 + lib/server.js | 3 ++- test/fixtures/with space.js | 1 + test/test-api.js | 4 ++-- test/test-bundle.js | 21 +++++++++++++++++- test/test-close-immediate.js | 5 +++-- test/test-server.js | 41 ++++++++++++++++++++++++++++++++++++ 8 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/with space.js create mode 100644 test/test-server.js diff --git a/index.js b/index.js index 7c78b2d..4f70552 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ var bole = require('bole') var log = bole('budo') var xtend = require('xtend') var budo = require('./lib/budo') -var path = require('path') +var url = require('url') module.exports = function(entry, opts) { var argv = xtend(opts) @@ -38,8 +38,8 @@ module.exports = function(entry, opts) { argv.port = typeof argv.port === 'number' ? argv.port : 9966 argv.dir = argv.dir || process.cwd() - argv.serve = file - + argv.serve = url.parse(file).path + if (typeof argv.dir !== 'string') return bail('--dir must be a path') @@ -75,7 +75,7 @@ module.exports = function(entry, opts) { if (parts.length > 1 && parts[1].length > 0) { return { from: parts[0], to: parts[1] } } - return { from: entry, to: path.basename(entry) } + return { from: entry, to: entry } } function bail(msg) { diff --git a/lib/budo.js b/lib/budo.js index 7bc4aa5..9710f67 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -224,5 +224,6 @@ function getWatchifyArgs(entries, opt) { delete opt['live-port'] delete opt['live-script'] delete opt['live-plugin'] + return entries.concat(dargs(opt)) } \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index f6b97ae..762bcdd 100644 --- a/lib/server.js +++ b/lib/server.js @@ -8,6 +8,7 @@ var fs = require('fs') var html = require('./default-index') var inject = require('inject-lr-script') var Emitter = require('events/') +var url = require('url') module.exports = function(opts) { var handler = createHandler(opts) @@ -51,7 +52,7 @@ function createHandler(opts) { port: opts['live-port'] } - router.addRoute('/' + opts.serve, function(req, res) { + router.addRoute('/' + url.parse(opts.serve).pathname, function(req, res) { log.info({ url: req.url, type: 'static' diff --git a/test/fixtures/with space.js b/test/fixtures/with space.js new file mode 100644 index 0000000..bf71f46 --- /dev/null +++ b/test/fixtures/with space.js @@ -0,0 +1 @@ +console.log('with space') \ No newline at end of file diff --git a/test/test-api.js b/test/test-api.js index 4da8f6a..ae70169 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -14,7 +14,7 @@ test('gets connect info', function(t) { }) .on('connect', function(ev) { t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') - t.equal(ev.serve, 'app.js', 'mapping matches') + t.equal(ev.serve, 'test/fixtures/app.js', 'mapping matches') t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') t.equal(ev.host, 'localhost', 'host is not specified') t.equal(ev.port, 8000, 'port matches') @@ -91,7 +91,7 @@ test('allow setting live() manually', function(t) { function testLive(t, app) { app - .once('reload', function(err) { + .once('reload', function() { //LiveReload triggered t.ok(true, 'got reload event') app.close() diff --git a/test/test-bundle.js b/test/test-bundle.js index a8ba11a..383e7a0 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -8,6 +8,7 @@ var watchifyArgs = require('watchify').args var path = require('path') var vm = require('vm') + test('serves app.js', run('test/fixtures/app.js')) test('entry mapping to bundle.js', run('test/fixtures/app.js:bundle.js')) test('turns off debug', run('test/fixtures/app', { debug: false })) @@ -27,6 +28,21 @@ test('bundles multiple and serves as static/bundle.js', run([ shouldServe: 'static/bundle.js' })) +test('does not break on query params', run('test/fixtures/with space.js:bundle.js?debug=true', { + shouldServe: 'bundle.js?debug=true', + message: 'with space' +})) + +test('serves with spaces and entry to bundle.js', run('test/fixtures/with space.js', { + serve: 'bundle.js', + shouldServe: 'bundle.js', + message: 'with space' +})) + +test('serves with spaces default', run('test/fixtures/with space.js', { + message: 'with space' +})) + function run(entries, opt) { return function(t) { matches(t, entries, opt) @@ -38,12 +54,14 @@ function matches(t, entries, opt) { var message = opt.message || 'from browserify' var shouldServe = opt.shouldServe + delete opt.message + delete opt.shouldServe + t.plan(shouldServe ? 5 : 4) var uri if (!Array.isArray(entries)) entries = [ entries ] - var app = budo(entries, opt) .on('connect', function(ev) { if (shouldServe) @@ -83,6 +101,7 @@ function matches(t, entries, opt) { .on('exit', function() { t.ok(true, 'closing') }) + .on('error', t.fail.bind(t)) function log(msg) { t.equal(msg, message, 'the output matches in both cases') diff --git a/test/test-close-immediate.js b/test/test-close-immediate.js index 22455e4..f8902fd 100644 --- a/test/test-close-immediate.js +++ b/test/test-close-immediate.js @@ -1,7 +1,9 @@ var test = require('tape') var budo = require('../') -var path = require('path') +//watchify, chokidar and tinylr are a bit stubborn +//if you try to close them immediately after starting +//the watchers. test('can close on connect with watch/live', function(t) { t.plan(2) t.timeoutAfter(10000) @@ -25,7 +27,6 @@ test('can close on connect with watch/live', function(t) { }) }) - test('can close on first update', function(t) { t.plan(3) t.timeoutAfter(10000) diff --git a/test/test-server.js b/test/test-server.js new file mode 100644 index 0000000..7cdbff2 --- /dev/null +++ b/test/test-server.js @@ -0,0 +1,41 @@ +var test = require('tape') +var budo = require('../') + +var request = require('request') +var entry = 'test/fixtures/app.js' + +test('default should serve on 9966', port(9966)) +test('should serve on specified port', port(3000, { port: 3000 })) + +test('should serve on --dir', function(t) { + t.plan(2) + var app = budo(entry, { dir: __dirname }) + .on('connect', function(ev) { + request.get({ + uri: ev.uri + 'fixtures/text.txt' + }, function(err, resp, body) { + if (err) t.fail(err) + t.equal(body.toString(), 'foobar', 'text matches') + app.close() + }) + }) + .on('exit', function() { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) +}) + +function port(expected, opt) { + return function(t) { + t.plan(2) + var app = budo(entry, opt) + .on('connect', function(ev) { + t.ok(ev.port, expected, 'serves on '+expected) + app.close() + }) + .on('exit', function() { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) + } +} \ No newline at end of file From b7db438cc6d3c7e382e0afe75d75aa5615ee7aca Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 13:27:31 -0400 Subject: [PATCH 073/302] cleaning up deps --- package.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/package.json b/package.json index bf304d6..f0de53b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "3.0.1-rc1", + "version": "3.0.0-rc2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { @@ -35,23 +35,16 @@ "brfs": "^1.4.0", "browserify": "^9.0.8", "canvas-fit": "^1.2.0", - "concat-stream": "^1.4.7", "domready": "^1.0.7", "garnish": "^2.1.2", "inject-lr-script": "^1.0.1", - "ndjson": "^1.3.0", - "npm-execspawn": "^1.0.6", "raf-loop": "^1.0.1", "request": "^2.53.0", - "rimraf": "^2.3.2", - "tap-finished": "0.0.1", "tap-spec": "^3.0.0", "tape": "^4.0.0", - "tree-kill": "0.0.6", "uglify-js": "^2.4.19", "vm": "0.0.1", "watchify": "^3.1.0", - "win-spawn": "^2.0.0" }, "scripts": { "test": "tape test/test*.js | tap-spec", From c355392851fea147111087cafa80045b5cb1fa09 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 13:27:51 -0400 Subject: [PATCH 074/302] comma in package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f0de53b..3562e3a 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "tape": "^4.0.0", "uglify-js": "^2.4.19", "vm": "0.0.1", - "watchify": "^3.1.0", + "watchify": "^3.1.0" }, "scripts": { "test": "tape test/test*.js | tap-spec", From 6945509922c396cdd111fcd5ff0e71683efb1109 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 13:54:43 -0400 Subject: [PATCH 075/302] updating docs and help --- README.md | 18 +++++------ bin/cmd.js | 5 --- docs/basics.md | 65 ++++++++++++++------------------------ docs/comparisons.md | 16 ++-------- docs/errors.md | 7 ++-- docs/programmatic-usage.md | 30 +++++++++++------- index.js | 5 +++ 7 files changed, 62 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index eafd284..e904a6c 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,20 @@ Note that budo needs a copy of `watchify` installed. It can be either local (pre npm install budo watchify -g ``` -The simplest use cases will start up a server with a default `index.html` and incrementally bundle your source on filesave. The requests are delayed until the bundle has finished, so you aren't served stale or empty bundles. Examples: +The simplest use cases will start up a server with a default `index.html` and incrementally bundle your source on filesave. The requests are delayed until the bundle has finished, so you won't be served stale or empty bundles if you refresh the page mid-update. Examples: ```sh -#run watchify on port 9966 +# serve file on port 9966 budo index.js -#run watchify with timing information +# show timing information on re-bundle budo index.js --verbose -#run watchify with some options and trigger LiveReload on JS/HTML/CSS file change +# transpile ES6 and trigger LiveReload on html/css/js change budo index.js --live --transform babelify ``` -You can open `localhost:9966` to see the content in action. +Then open [http://localhost:9966](http://localhost:9966) to see the content in action. To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used: @@ -31,9 +31,7 @@ To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bi budo index.js | garnish ``` -See [docs](#docs) for more features. - -PRs/suggestions/comments welcome. +See [docs](#docs) for more features. PRs/suggestions/comments welcome. ## docs @@ -51,7 +49,7 @@ PRs/suggestions/comments welcome. ### CLI -Details for `budo` command-line interface. Other options like `--verbose` and `--transform` are sent to browserify/watchify. +Details for `budo` command-line interface. Other options like `--transform` are sent to browserify/watchify. ```sh Usage: @@ -69,7 +67,7 @@ Options: --poll=N use polling for file watch, with optional interval N ``` -By default, the `--debug` option will be sent to watchify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. +By default, the `--debug` option will be sent to browserify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. ### API diff --git a/bin/cmd.js b/bin/cmd.js index 7749dfd..e593938 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -28,11 +28,6 @@ if (showHelp) { return } -if (opts.o || opts.outfile) { - console.error('ERROR:\n --outfile has been removed in budo@3.0') - return -} - var basePort = opts.port || 9966 getport(basePort, function(err, port) { if (err) { diff --git a/docs/basics.md b/docs/basics.md index 6189af3..d5f6ce8 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -2,89 +2,72 @@ budō allows you to get your scripts up and running quickly in a local environment. -First, you will need [NodeJS and npm](http://nodejs.org/download/). Then you can install the tools globally like so: +First, you will need [NodeJS and npm](http://nodejs.org/download/). Then you can install the tools globally like so. ```sh npm install budo watchify garnish -g ``` -Later we will explore the benefits of saving these locally, but for now it's easier to get up and running if they are global. - -The next step is to create an index.js file and start serving it with budō: +Now, serve a file like so. We are piping the output to [garnish](https://github.com/mattdesl/garnish) for prettier logging, but this is optional. ```sh -cd my-cool-project -touch index.js -budo index.js --outfile bundle.js --verbose +budo index.js --verbose | garnish ``` -Now if you open `localhost:9966` in your browser, it will serve you with the bundled output of `index.js`. - -When you save `index.js`, the script will be re-built into the `bundle.js` output file. Watchify will also print the time elapsed into console. +Open [http://localhost:9966/](http://localhost:9966/) to see the bundled result of `index.js`. -## pretty-printing - -Earlier we installed [garnish](https://github.com/mattdesl/garnish), which will give us a nicer output in terminal. We can use it like so: - -```sh -budo index.js --outfile bundle.js | garnish -``` +Saving `index.js` will be incremental, which means it will be fast even if your app spans hundreds of modules. ## index.html Notice we haven't had to write any HTML! If you want to, though, you can drop `index.html` in the same folder that you are serving budō from (or `--dir` folder), and it will use that instead of a dynamically generated index. -## local installation - -Although global install gets us up and running quickly, it's preferred to save these tools as `devDependencies` in your projects so that others cloning your repo are working with the same versions as you. To do that, you need a `package.json`, which you can create like so: +The `src` for your script tag should match the entry point you gave. -```sh -npm init +```html + ``` -Then, you can save the tools locally: +You can specify a different end point for the serve like so: ```sh -npm install budo watchify garnish --save-dev +budo index.js:static/bundle.js | garnish ``` -You can't run local tools with a simple terminal command; instead, we need to use [npm-scripts](https://docs.npmjs.com/misc/scripts). Open up your package.json and update `"scripts"` so it looks like this: +Your HTML would the look like this: -```sh - "scripts": { - "dev": "budo index.js -o bundle.js --live | garnish" - }, +```html + ``` -Now users cloning your repo can run the following to start serving the file: +## local installation + +If you are using these in your modules for demos/etc, you should save them globally so that others can get the same versions when they `git clone` and `npm install` your repo. ```sh -npm install -npm run dev +npm install budo watchify garnish --save-dev ``` -## temp directory - -If you don't specify an `--outfile` or `-o` argument, budō will save a `bundle.js` to a temporary directory that gets destroyed upon closing the server. +For local tools, we need to use [npm-scripts](https://docs.npmjs.com/misc/scripts). Open up your package.json and update `"scripts"` so it looks like this: ```sh -budo index.js --verbose | garnish + "scripts": { + "start": "budo index.js --verbose | garnish" + }, ``` -You can also change the mapping like so, which allows you to specify a different entry point in your `index.html`: +Now running the following will start the development server: ```sh -budo index.js:static.js | garnish +npm run start ``` -This is good for quick prototyping, but the `--outfile` approach is more robust and cross-platform, and thus preferred when delivering budō as a local dependency. - ## live reload budō also includes support for [LiveReload](livereload.com). The `--live` argument injects a script tag into your HTML file so you can reload across many devices and browsers. ```sh -budo index.js --outfile bundle.js --live | garnish +budo index.js --live | garnish ``` Now when you save the `index.js` file, it will trigger a live-reload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. diff --git a/docs/comparisons.md b/docs/comparisons.md index 95df283..bf51a58 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -2,11 +2,11 @@ ## budō -[budō](https://github.com/mattdesl/budo) lies somewhere between the rich feature set of [Beefy](#beefy) and the small focus of [wzrd](#wzrd). It spawns a watchify process, produces ndjson logs, and integrates with LiveReload (including CSS injection). It is also the base for more experimental features like [Chrome script injection](https://github.com/mattdesl/budo-chrome), and rapid prototyping. +[budō](https://github.com/mattdesl/budo) lies somewhere between the rich feature set of [Beefy](#beefy) and the small focus of [wzrd](#wzrd). It has a stronger focus on LiveReload and incremental bundling, suspending the server response until the bundle is complete (i.e. never serves stale bundles). It is also the base for more experimental features like [Chrome script injection](https://github.com/mattdesl/budo-chrome) and [React hot module replacement](https://github.com/milankinen/livereactload). ## beefy -[Beefy](https://github.com/chrisdickinson/beefy) is a feature-rich dev tool for browserify, and much of the inspiration for this project. It has a wide scope, encompassing browserify and watchify, and takes a different approach to bundling by using watchify's programmatic API rather than [execspawn](https://www.npmjs.com/package/npm-execspawn). +[Beefy](https://github.com/chrisdickinson/beefy) is a feature-rich dev tool for browserify, and much of the inspiration for this project. It has a wide scope, encompassing browserify and watchify, and takes a different approach to live reload. ```sh #example ... @@ -24,18 +24,6 @@ Incremental bundling is likely outside of its scope. wzrd index.js:bundle.js | garnish ``` -## wtch - -[wtch](https://github.com/mattdesl/wtch) is a small live-reload utility not related to budo or watchify. It watches for JS/CSS/HTML changes and triggers a live-reload event. It can be used to augment wzrd with some watching capabilities. - -```sh -#example ... -wzrd index.js:bundle.js | wtch | garnish - -#with watchify ... -watchify index.js -o bundle.js | wtch bundle.js | garnish -``` - ## garnish [garnish](https://github.com/mattdesl/garnish) simply prettifies bole and ndjson log output from tools that decide to use it. This includes wzrd, wtch, and budō. \ No newline at end of file diff --git a/docs/errors.md b/docs/errors.md index 6158d32..ad42d12 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -8,9 +8,12 @@ For clearer error reporting, you can use the [errorify](https://github.com/zerto ```sh # install npm install errorify --save-dev +``` -# include it with --plugin or -p -budo index.js --live -p errorify | garnish +```json + "scripts": { + "start": "budo index.js --live -p errorify | garnish" + } ``` Using [babelify](https://www.npmjs.com/package/babelify) and errorify together, the error message looks like this: diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md index 95ff50b..ebb8449 100644 --- a/docs/programmatic-usage.md +++ b/docs/programmatic-usage.md @@ -36,24 +36,30 @@ Called once the budo server connects. The callback is passed an `event` object t ```js { - uri: 'http://localhost:9966/', //served URI - from: 'app/bundle.js', //the actual path to the bundle - to: 'bundle.js', //the mapping server expects - glob: 'app/bundle.js', //file glob for bundle.js - dir: 'app', //the working directory - host: 'localhost', //defaults to localhost - port: 9966, //the port we're running on + uri: 'http://localhost:9966/', //served URI + serve: 'bundle/entry%20file.js' //the URL path for our entry file + dir: 'app', //the working directory being served + host: 'localhost', //defaults to localhost + port: 9966, //the port we're running on } ``` -#### `b.on('watch')` +#### `b.on('update')` + +This event is triggered when the bundle changes. It is passed the following: + +```(urlPath, src)``` -If file watching is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after a file change or addition is made (such as bundle.js or themes.css). The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. +Where `urlPath` is the served name (e.g. `bundle%20file.js`) and `src` is the new contents of the bundle. #### `b.on('reload')` If live reload is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. +#### `b.on('watch')` + +If file watching is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after HTML and CSS files have changed. The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. + #### `b.reload(path)` If live reload is enabled (i.e. through `live` or `live-plugin`), this will send a LiveReload event to the given path and then trigger the `"reload"` event. @@ -66,9 +72,9 @@ If `live` and `live-plugin` were not specified, you can manually enable the Live If `live` and `live-plugin` were not specified, you can manually enabe [chokidar's](https://github.com/paulmillr/chokidar) file watching with the specified `globs` (array or string) and options. -`globs` defaults to watching `**/*.{html,css}` and the watchified bundle. `chokidarOpts` defaults to the options passed to the budo constructor. +`globs` defaults to watching `**/*.{html,css}`. `chokidarOpts` defaults to the options passed to the budo constructor. -Example of using `live()` and `watch()` together. +Example of using `live()` and `watch()` together. This will only trigger LiveReload on CSS updates. ```js var budo = require('budo') @@ -106,7 +112,7 @@ gulp.task('dev', function(cb) { }) ``` -Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and write `bundle.js` to a temp directory. +Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and serve the browserified contents of `index.js`. #### integrations diff --git a/index.js b/index.js index 4f70552..6e0e1ad 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,11 @@ module.exports = function(entry, opts) { var emitter = budo() + + if (opts.o || opts.outfile) { + log.warn('--outfile has been removed in budo@3.0') + } + var entries = Array.isArray(entry) ? entry : [entry] entries = entries.filter(Boolean) if (entries.length === 0) { From e74cef4e0b0478cd2be93e4d0271700f9a9c2aa2 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 9 Apr 2015 13:55:52 -0400 Subject: [PATCH 076/302] correct issue with argv / opts --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6e0e1ad..fd80e94 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,7 @@ module.exports = function(entry, opts) { var emitter = budo() - if (opts.o || opts.outfile) { + if (argv.o || argv.outfile) { log.warn('--outfile has been removed in budo@3.0') } From 022cb2ee214d776d4162115a73135dfb67c120e8 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 11:22:53 -0400 Subject: [PATCH 077/302] do not submit outfile to watchify --- index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.js b/index.js index fd80e94..48a7cd1 100644 --- a/index.js +++ b/index.js @@ -19,6 +19,9 @@ module.exports = function(entry, opts) { if (argv.o || argv.outfile) { log.warn('--outfile has been removed in budo@3.0') + //ensure we don't pass to watchify + delete argv.o + delete argv.outfile } var entries = Array.isArray(entry) ? entry : [entry] From a2253ce34055e083303f825a6d77c28a0ee1bd4d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 11:51:36 -0400 Subject: [PATCH 078/302] allow default index to be overridden by API --- index.js | 3 +-- lib/default-index.js | 2 +- lib/server.js | 8 +++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 48a7cd1..660caff 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ var url = require('url') module.exports = function(entry, opts) { var argv = xtend(opts) - + if (argv.stream) { bole.output({ stream: argv.stream, @@ -16,7 +16,6 @@ module.exports = function(entry, opts) { var emitter = budo() - if (argv.o || argv.outfile) { log.warn('--outfile has been removed in budo@3.0') //ensure we don't pass to watchify diff --git a/lib/default-index.js b/lib/default-index.js index 3bdbd21..bc06cb6 100644 --- a/lib/default-index.js +++ b/lib/default-index.js @@ -6,7 +6,7 @@ module.exports = function(opt) { '', '', '', - '', + '', '', '' ].join('')) diff --git a/lib/server.js b/lib/server.js index 762bcdd..76725ff 100644 --- a/lib/server.js +++ b/lib/server.js @@ -5,10 +5,10 @@ var fs = require('fs') var path = require('path') var log = require('bole')('budo') var fs = require('fs') -var html = require('./default-index') var inject = require('inject-lr-script') var Emitter = require('events/') var url = require('url') +var defaultIndex = require('./default-index') module.exports = function(opts) { var handler = createHandler(opts) @@ -115,8 +115,10 @@ function createHandler(opts) { function generateIndex(outfile, req, res) { res.setHeader('content-type', 'text/html') - html({ - outfile: outfile + + var stream = opts.defaultIndex || defaultIndex + stream({ + entry: outfile }).pipe(res) } } \ No newline at end of file From 536fca9cf735072ed7eeb31653d13ca3a8c79b88 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 11:54:53 -0400 Subject: [PATCH 079/302] keeping it dry --- lib/budo.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 9710f67..7ab0654 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -214,16 +214,13 @@ function getWatchifyArgs(entries, opt) { } //clean up some possible collisions - delete opt.dir - delete opt.o - delete opt.outfile - delete opt.port - delete opt.host - delete opt.live - delete opt.serve - delete opt['live-port'] - delete opt['live-script'] - delete opt['live-plugin'] - + var collisions = [ + 'dir', 'o', 'outfile', 'port', + 'host', 'live', 'serve', 'live-port', + 'live-plugin', 'default-index' + ] + collisions.forEach(function(col) { + delete opt[col] + }) return entries.concat(dargs(opt)) } \ No newline at end of file From 8943d98cde8a5cc65d980372ad32cd076c90e6dc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 11:55:36 -0400 Subject: [PATCH 080/302] use default-index for now --- lib/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index 76725ff..daf0ed1 100644 --- a/lib/server.js +++ b/lib/server.js @@ -115,8 +115,8 @@ function createHandler(opts) { function generateIndex(outfile, req, res) { res.setHeader('content-type', 'text/html') - - var stream = opts.defaultIndex || defaultIndex + + var stream = opts['default-index'] || defaultIndex stream({ entry: outfile }).pipe(res) From a681021f780d5996c1d472d900756e3d1d644bab Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 11:56:17 -0400 Subject: [PATCH 081/302] camel case is better since its API-only --- lib/budo.js | 2 +- lib/server.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 7ab0654..cb7ef1b 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -217,7 +217,7 @@ function getWatchifyArgs(entries, opt) { var collisions = [ 'dir', 'o', 'outfile', 'port', 'host', 'live', 'serve', 'live-port', - 'live-plugin', 'default-index' + 'live-plugin', 'defaultIndex' ] collisions.forEach(function(col) { delete opt[col] diff --git a/lib/server.js b/lib/server.js index daf0ed1..220a3bf 100644 --- a/lib/server.js +++ b/lib/server.js @@ -116,7 +116,7 @@ function createHandler(opts) { function generateIndex(outfile, req, res) { res.setHeader('content-type', 'text/html') - var stream = opts['default-index'] || defaultIndex + var stream = opts.defaultIndex || defaultIndex stream({ entry: outfile }).pipe(res) From 59b386ad30f9d12b10a81c66578b006c6a42fe8c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 10 Apr 2015 12:11:55 -0400 Subject: [PATCH 082/302] bump rc --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3562e3a..6e4635e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "3.0.0-rc2", + "version": "3.0.0-rc3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 8671f2eea8766fbecd91d96c1557f78c7a507e5a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 12:52:31 -0400 Subject: [PATCH 083/302] allowing stream to be disabled via CLI, updating docs and tests for stream --- README.md | 6 ++++-- bin/cmd.js | 11 +++++++++-- index.js | 2 +- package.json | 7 ++++--- test/test-api.js | 20 ++++++++++++++++++++ test/test-bundle.js | 1 - 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e904a6c..9c14b93 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,15 @@ Options: --live-port the LiveReload port, default 35729 --verbose, -v verbose timing information for re-bundles --poll=N use polling for file watch, with optional interval N + --no-stream do not print messages to stdout + --no-debug do not use inline source maps ``` -By default, the `--debug` option will be sent to browserify (for source maps). If this is unwanted, you can use `--no-debug` or `--debug=false` to disable source maps. +By default, messages will be printed to `stdout` and `debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. ### API -The API mirrors the CLI except you must provide a `stream` for logging, and it does not attempt to auto-portfind. +The API mirrors the CLI except it does not write to `process.stdout` by default, and does not attempt to find available ports from a base port. ```js var budo = require('budo') diff --git a/bin/cmd.js b/bin/cmd.js index e593938..c4a9e59 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -5,11 +5,18 @@ //Uses auto port-finding var args = process.argv.slice(2) -var opts = require('minimist')(args) var getport = require('getport') +var opts = require('minimist')(args, { + boolean: ['stream', 'debug'], + default: { stream: true, debug: true } +}) + +//user can silent budo with --no-stream +if (opts.stream !== false) { + opts.stream = process.stdout +} var entries = opts._ -opts.stream = process.stdout delete opts._ var showHelp = opts.h || opts.help diff --git a/index.js b/index.js index 660caff..2608d17 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,7 @@ module.exports = function(entry, opts) { var emitter = budo() if (argv.o || argv.outfile) { - log.warn('--outfile has been removed in budo@3.0') + console.error('Warning: --outfile has been removed in budo@3.0') //ensure we don't pass to watchify delete argv.o delete argv.outfile diff --git a/package.json b/package.json index 6e4635e..f69b01b 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,10 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/*.js --dir example --verbose | garnish", - "live": "./bin/cmd.js example/*.js --dir example --live -v | garnish -v", - "live-plugin": "./bin/cmd.js example/*.js --dir example --live-plugin -v | garnish", + "silent": "./bin/cmd.js example/app.js:bundle.js --dir example --stream false", + "start": "./bin/cmd.js example/app.js:bundle.js --dir example --verbose | garnish", + "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -v | garnish -v", + "live-plugin": "./bin/cmd.js example/app.js:bundle.js --dir example --live-plugin -v | garnish", "remap": "./bin/cmd.js example/*.js --serve bundle2.js --dir example --live -v | garnish" }, "keywords": [ diff --git a/test/test-api.js b/test/test-api.js index ae70169..7068655 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -1,5 +1,6 @@ var test = require('tape') var budo = require('../') +var through = require('through2') test('gets connect info', function(t) { t.plan(7) @@ -72,6 +73,25 @@ test('sets watch() and live() by default with live: true', function(t) { testLive(t, app) }) +test('should pipe JSON to specified stream', function(t) { + t.plan(3) + t.timeoutAfter(10000) + + var app = budo('test/fixtures/app.js', { + dir: __dirname, + stream: through(function(buf) { + t.ok(buf.length > 0, 'got some message') + t.doesNotThrow(function() { + return JSON.parse(buf.toString()) + }, 'produces valid JSON') + app.close() + }) + }) + .on('exit', function() { + t.ok(true, 'got exit event') + }) +}) + test('allow setting live() manually', function(t) { t.plan(3) t.timeoutAfter(10000) diff --git a/test/test-bundle.js b/test/test-bundle.js index 383e7a0..b4fd35f 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -8,7 +8,6 @@ var watchifyArgs = require('watchify').args var path = require('path') var vm = require('vm') - test('serves app.js', run('test/fixtures/app.js')) test('entry mapping to bundle.js', run('test/fixtures/app.js:bundle.js')) test('turns off debug', run('test/fixtures/app', { debug: false })) From 91b3b8ed9b1fc497583d3e8a62868d6b407e75cc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 12:56:17 -0400 Subject: [PATCH 084/302] fix typo --- docs/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index d5f6ce8..2ad9a41 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -42,7 +42,7 @@ Your HTML would the look like this: ## local installation -If you are using these in your modules for demos/etc, you should save them globally so that others can get the same versions when they `git clone` and `npm install` your repo. +If you are using these in your modules for demos/etc, you should save them locally so that others can get the same versions when they `git clone` and `npm install` your repo. ```sh npm install budo watchify garnish --save-dev From b37c84172ae032b49c27fb80868eb03058bd289c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 12:58:59 -0400 Subject: [PATCH 085/302] no longer using ecstatic or caching for bundles --- docs/basics.md | 12 +++--------- example/app.js | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/basics.md b/docs/basics.md index 2ad9a41..1697020 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -64,18 +64,12 @@ npm run start ## live reload -budō also includes support for [LiveReload](livereload.com). The `--live` argument injects a script tag into your HTML file so you can reload across many devices and browsers. +budō also includes support for [LiveReload](livereload.com). The `--live` argument injects a script tag into your HTML file and listens for a live reload server. ```sh budo index.js --live | garnish ``` -Now when you save the `index.js` file, it will trigger a live-reload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. +Now when you save the `index.js` file, it will trigger a LiveReload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. -Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). - -## caching - -For the best experience, make sure that browser caching is disabled during development. Otherwise you might accidentally get served the empty source file mid-bundle, and subsequent reloads will do nothing. In Chrome you can disable it by hitting the Gears button in the DevTools, and checking `Disable Cache (while DevTools is open)` - -![cache](http://i.imgur.com/e1LpJJi.png) +Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). \ No newline at end of file diff --git a/example/app.js b/example/app.js index b76ffc6..54320d8 100644 --- a/example/app.js +++ b/example/app.js @@ -22,7 +22,7 @@ require('raf-loop')(function(dt) { ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 50, 20, 20) + ctx.fillRect(Math.sin(time)*50 + 300, 50, 20, 40) ctx.fillText("from browserify!", 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) From d2a63426ce2cb800486a9205359289cb984c2a8c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 14:50:55 -0400 Subject: [PATCH 086/302] fixing args for API and CLI --- bin/cmd.js | 49 +-------------- index.js | 135 ++++++++++++++++------------------------- lib/budo.js | 45 ++------------ lib/create-watchify.js | 97 +++++++++++++++++++++++++++++ lib/get-watchify.js | 22 ------- lib/index.js | 94 ++++++++++++++++++++++++++++ lib/watchify.js | 12 ++-- test/test-args.js | 81 +++++++++++++++++++++++++ test/test-bundle.js | 4 +- 9 files changed, 339 insertions(+), 200 deletions(-) create mode 100644 lib/create-watchify.js delete mode 100644 lib/get-watchify.js create mode 100644 lib/index.js create mode 100644 test/test-args.js diff --git a/bin/cmd.js b/bin/cmd.js index c4a9e59..a1b1f2d 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -3,52 +3,5 @@ //Starts budo with stdout //Handles --help and error messaging //Uses auto port-finding - var args = process.argv.slice(2) -var getport = require('getport') -var opts = require('minimist')(args, { - boolean: ['stream', 'debug'], - default: { stream: true, debug: true } -}) - -//user can silent budo with --no-stream -if (opts.stream !== false) { - opts.stream = process.stdout -} - -var entries = opts._ -delete opts._ - -var showHelp = opts.h || opts.help - -if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { - console.error('ERROR:\n no entry scripts specified\n use --help for examples') - process.exit(1) -} - -if (showHelp) { - var vers = require('../package.json').version - console.log('budo ' + vers, '\n') - var help = require('path').join(__dirname, 'help.txt') - require('fs').createReadStream(help) - .pipe(process.stdout) - return -} - -var basePort = opts.port || 9966 -getport(basePort, function(err, port) { - if (err) { - console.error("Could not find available port", err) - process.exit(1) - } - opts.port = port - require('../')(entries, opts) - .on('error', function(err) { - //Some more helpful error messaging - if (err.message === 'listen EADDRINUSE') - console.error("Port", port, "is not available\n") - else - console.error('Error:\n ' + err.message) - process.exit(1) - }) -}) \ No newline at end of file +require('../').cli(args) \ No newline at end of file diff --git a/index.js b/index.js index 2608d17..f3a7477 100644 --- a/index.js +++ b/index.js @@ -1,94 +1,61 @@ -var bole = require('bole') -var log = bole('budo') -var xtend = require('xtend') -var budo = require('./lib/budo') -var url = require('url') - -module.exports = function(entry, opts) { - var argv = xtend(opts) - - if (argv.stream) { - bole.output({ - stream: argv.stream, - level: 'debug' - }) - } - - var emitter = budo() - - if (argv.o || argv.outfile) { - console.error('Warning: --outfile has been removed in budo@3.0') - //ensure we don't pass to watchify - delete argv.o - delete argv.outfile - } - - var entries = Array.isArray(entry) ? entry : [entry] - entries = entries.filter(Boolean) - if (entries.length === 0) { - return bail("No entry scripts specified!") - } - - //clean up entries and take the first one for bundle mapping - var file - entries = entries.map(function(entry, i) { - var map = mapping(entry) - if (i === 0) - file = map.to - return map.from +var create = require('./lib') + +//public programmatic API +// passes camel case to browserify +// also supports objects for transform etc +module.exports = create + +//CLI API (undocumented, private for now) +// uses watchify/bin/args to parse dash-case +// only expects strings for transform etc +// also prints to stdout by default, and +// uses portfinder after base port is taken +module.exports.cli = function cli(args) { + var getport = require('getport') + var opts = require('minimist')(args, { + boolean: ['stream', 'debug'], + default: { stream: true, debug: true } }) - //if user specified -o use that as our entry map - var serveAs = argv.serve - if (serveAs && typeof serveAs === 'string') - file = serveAs - - argv.port = typeof argv.port === 'number' ? argv.port : 9966 - argv.dir = argv.dir || process.cwd() - argv.serve = url.parse(file).path - - if (typeof argv.dir !== 'string') - return bail('--dir must be a path') + //user can silent budo with --no-stream + if (opts.stream !== false) { + opts.stream = process.stdout + } - //run watchify server - emitter.on('connect', setupLive) - emitter._start(entries, argv) - .on('exit', function() { - log.info('closing') - }) + var entries = opts._ + delete opts._ - return emitter + var showHelp = opts.h || opts.help - //if user requested live: true, set it up with some defaults - function setupLive() { - if (argv.live || argv['live-plugin']) { - emitter - .watch() - .live() - .on('watch', function(ev, file) { - //HTML/CSS changes - if (ev === 'change' || ev === 'add') - emitter.reload(file) - }) - .on('update', function(file) { - //bundle.js changes - emitter.reload(file) - }) - } + if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { + console.error('ERROR:\n no entry scripts specified\n use --help for examples') + process.exit(1) } - function mapping(entry) { - var parts = entry.split(':') - if (parts.length > 1 && parts[1].length > 0) { - return { from: parts[0], to: parts[1] } - } - return { from: entry, to: entry } + if (showHelp) { + var vers = require('../package.json').version + console.log('budo ' + vers, '\n') + var help = require('path').join(__dirname, 'help.txt') + require('fs').createReadStream(help) + .pipe(process.stdout) + return } - function bail(msg) { - process.nextTick(function() { - emitter.emit('error', new Error(msg)) - }) - return emitter - } + var basePort = opts.port || 9966 + getport(basePort, function(err, port) { + if (err) { + console.error("Could not find available port", err) + process.exit(1) + } + opts.port = port + create(entries, opts, true) + .on('error', function(err) { + //Some more helpful error messaging + if (err.message === 'listen EADDRINUSE') + console.error("Port", port, "is not available\n") + else + console.error('Error:\n ' + err.message) + process.exit(1) + }) + }) } \ No newline at end of file diff --git a/lib/budo.js b/lib/budo.js index cb7ef1b..fbbb779 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -2,7 +2,6 @@ var Emitter = require('events/') var xtend = require('xtend') var assign = require('xtend/mutable') var log = require('bole')('budo') -var dargs = require('dargs') var createServer = require('./server') var createFileWatch = require('./file-watch') @@ -26,7 +25,7 @@ module.exports = function() { var defaultWatchOpts var deferreds = [] - emitter._start = function(entries, opt) { + emitter._start = function(entries, opt, cli) { opt = opt || {} var port = opt.port server = createServer(opt) @@ -52,7 +51,7 @@ module.exports = function() { : opt['ignore-watch'] defaultWatchOpts = { poll: opt.poll, - 'ignore-watch': ignoreWatchArg + 'ignore-watch': opt.ignoreWatch || ignoreWatchArg } //and default live options defaultLiveOpts = { @@ -62,7 +61,7 @@ module.exports = function() { } //start watchify process - runWatchify(entries, opt) + runWatchify(entries, opt, cli) serveAs = opt.serve started = true @@ -144,15 +143,13 @@ module.exports = function() { return emitter - function runWatchify(entries, opt) { + function runWatchify(entries, opt, cli) { if (closed) return - //get watchify args (with --debug by default) - var watchifyArgs = getWatchifyArgs(entries, opt) - //create a new watchify instance - watchify = createWatchify(watchifyArgs, { + watchify = createWatchify(entries, opt, { + cli: cli, //whether to use watchify/bin/args or not dir: opt.dir, serve: opt.serve, verbose: opt.v || opt.verbose, @@ -194,33 +191,3 @@ function getChokidarOpts(opt) { } return opt } - -function getWatchifyArgs(entries, opt) { - //do not mutate original opts - opt = assign({}, opt) - - //disable delay since we will handle debouncing manually - opt.delay = 0 - - //enable debug by default - if (opt.d !== false && opt.debug !== false) { - delete opt.d - opt.debug = true - } - //if user explicitly disabled debug... - else if (opt.d === false || opt.debug === false) { - delete opt.d - delete opt.debug - } - - //clean up some possible collisions - var collisions = [ - 'dir', 'o', 'outfile', 'port', - 'host', 'live', 'serve', 'live-port', - 'live-plugin', 'defaultIndex' - ] - collisions.forEach(function(col) { - delete opt[col] - }) - return entries.concat(dargs(opt)) -} \ No newline at end of file diff --git a/lib/create-watchify.js b/lib/create-watchify.js new file mode 100644 index 0000000..0483e05 --- /dev/null +++ b/lib/create-watchify.js @@ -0,0 +1,97 @@ +var resolve = require('resolve') +var getModule = require('resolve-npm-which') +var path = require('path') +var assign = require('xtend/mutable') +var dargs = require('dargs') + +//finds watchify in local or global +//also handles whether to use bin/args or not (for CLI) +module.exports = function (entries, userArgs, opt, cb) { + var useFromArgs = opt.cli + + getModule('watchify', opt, function(err, watchifyDir) { + if (err) + return cb(err) + + //create a watchify instance manually + //(supports transform/plugin as objects) + if (!useFromArgs) { + resolve('browserify', { + basedir: watchifyDir + }, function(err, browserifyModule) { + if (err) + return cb(new Error('could not find watchify/node_modules/browserify - unsupported watchify version')) + cb(null, fromAPI(browserifyModule, watchifyDir, entries, userArgs)) + }) + } + //otherwise let watchify parse the CLI options for us + //(does not support transform/plugin as objects) + else { + resolve('./bin/args.js', { + basedir: watchifyDir + }, function(err, fromArgsModule) { + if (err) { + return cb(new Error('could not find watchify/bin/args.js - unsupported watchify version')) + } + cb(null, fromCLI(fromArgsModule, entries, userArgs)) + }) + } + }) +} + +function fromAPI(browserifyDir, watchifyDir, entries, userArgs) { + var watchify = require(watchifyDir) + var browserify = require(browserifyDir) + + var b = browserify(getDefaultArgs(userArgs)) + var instance = watchify(b) + entries.forEach(function(entry) { + instance.add(path.resolve(entry)) + }) + return instance +} + +function fromCLI(dir, entries, userArgs) { + var cliArgs = getCLIArgs(entries, userArgs) + var fromArgs = require(dir) + return fromArgs(cliArgs) +} + +function getDefaultArgs(opt) { + //ensure we have watchify args setup + opt = assign({ cache: {}, packageCache: {} }, opt) + + //disable delay since we will handle debouncing manually + opt.delay = 0 + + //enable debug by default + if (opt.d !== false && opt.debug !== false) { + delete opt.d + opt.debug = true + } + //if user explicitly disabled debug... + else if (opt.d === false || opt.debug === false) { + delete opt.d + delete opt.debug + } + + //clean up some possible collisions with budo + removeCollisions(opt) + return opt +} + +function getCLIArgs(entries, opt) { + opt = getDefaultArgs(opt) + return entries.concat(dargs(opt)) +} + +function removeCollisions(opt) { + var collisions = [ + 'dir', 'o', 'outfile', 'port', + 'host', 'live', 'serve', 'live-port', + 'live-plugin', 'defaultIndex' + ] + collisions.forEach(function(col) { + delete opt[col] + }) +} \ No newline at end of file diff --git a/lib/get-watchify.js b/lib/get-watchify.js deleted file mode 100644 index edb2ef4..0000000 --- a/lib/get-watchify.js +++ /dev/null @@ -1,22 +0,0 @@ -var resolve = require('resolve') -var getModule = require('resolve-npm-which') - -//finds watchify/bin/args.js local or global -module.exports = function getWatchify(opt, cb) { - if (typeof opt === 'function') { - cb = opt - opt = { basedir: process.cwd() } - } - getModule('watchify', opt, function(err, result) { - if (err) - return cb(err) - - //Once we get watchify path, grab its bin/args.js - resolve('./bin/args.js', { basedir: result }, function(err, watchifyModule) { - if (err) - return cb(new Error('could not find watchify/bin/args.js - unsupported watchify version')) - var fromArgs = require(watchifyModule) - cb(null, fromArgs) - }) - }) -} \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..37b8e7d --- /dev/null +++ b/lib/index.js @@ -0,0 +1,94 @@ +var bole = require('bole') +var log = bole('budo') +var xtend = require('xtend') +var budo = require('./budo') +var url = require('url') + +module.exports = function createBudo(entry, opts, cli) { + var argv = xtend(opts) + + if (argv.stream) { + bole.output({ + stream: argv.stream, + level: 'debug' + }) + } + + var emitter = budo() + + if (argv.o || argv.outfile) { + console.error('Warning: --outfile has been removed in budo@3.0') + //ensure we don't pass to watchify + delete argv.o + delete argv.outfile + } + + var entries = Array.isArray(entry) ? entry : [entry] + entries = entries.filter(Boolean) + if (entries.length === 0) { + return bail("No entry scripts specified!") + } + + //clean up entries and take the first one for bundle mapping + var file + entries = entries.map(function(entry, i) { + var map = mapping(entry) + if (i === 0) + file = map.to + return map.from + }) + + //if user specified -o use that as our entry map + var serveAs = argv.serve + if (serveAs && typeof serveAs === 'string') + file = serveAs + + argv.port = typeof argv.port === 'number' ? argv.port : 9966 + argv.dir = argv.dir || process.cwd() + argv.serve = url.parse(file).path + + if (typeof argv.dir !== 'string') + return bail('--dir must be a path') + + //run watchify server + emitter.on('connect', setupLive) + emitter._start(entries, argv, cli) + .on('exit', function() { + log.info('closing') + }) + + return emitter + + //if user requested live: true, set it up with some defaults + function setupLive() { + if (argv.live || argv['live-plugin']) { + emitter + .watch() + .live() + .on('watch', function(ev, file) { + //HTML/CSS changes + if (ev === 'change' || ev === 'add') + emitter.reload(file) + }) + .on('update', function(file) { + //bundle.js changes + emitter.reload(file) + }) + } + } + + function mapping(entry) { + var parts = entry.split(':') + if (parts.length > 1 && parts[1].length > 0) { + return { from: parts[0], to: parts[1] } + } + return { from: entry, to: entry } + } + + function bail(msg) { + process.nextTick(function() { + emitter.emit('error', new Error(msg)) + }) + return emitter + } +} \ No newline at end of file diff --git a/lib/watchify.js b/lib/watchify.js index 31db1c7..80b43ac 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -1,11 +1,11 @@ var log = require('bole')('budo') -var getWatchify = require('./get-watchify') +var createWatchify = require('./create-watchify') var Emitter = require('events/') var debounce = require('debounce') var concat = require('concat-stream') //Eventually this may split into a watchify-server module -module.exports = function(watchifyArgs, opt) { +module.exports = function(entries, userArgs, opt) { var emitter = new Emitter() var delay = opt.delay var closed = false @@ -29,7 +29,10 @@ module.exports = function(watchifyArgs, opt) { } } - getWatchify({ basedir: opt.dir }, function(err, fromArgs) { + createWatchify(entries, userArgs, { + cli: opt.cli, + basedir: opt.dir + }, function(err, instance) { if (err) { var msg = [ err.message, @@ -42,8 +45,7 @@ module.exports = function(watchifyArgs, opt) { if (closed) return - watchify = fromArgs(watchifyArgs) - + watchify = instance var bundleDebounced = debounce(bundle, delay) watchify.on('update', function() { emitter.emit('pending') diff --git a/test/test-args.js b/test/test-args.js new file mode 100644 index 0000000..ea43ad4 --- /dev/null +++ b/test/test-args.js @@ -0,0 +1,81 @@ +var test = require('tape') +var budo = require('../') +var browserify = require('browserify') +var watchifyArgs = require('watchify').args +var path = require('path') +var vm = require('vm') +var brfs = require('brfs') +var xtend = require('xtend') + +var entry = 'test/fixtures/app-brfs.js' + +test('gets connect info', function(t) { + t.plan(1) + t.timeoutAfter(10000) + + var bundler = browserify(xtend(watchifyArgs, { + dbeug: false, + transform: brfs, + fullPaths: false, + insertGlobals: true + })) + + bundler.add(path.resolve(entry)) + + bundler.bundle(function(err, expected) { + if (err) return t.fail(err) + + var app = budo(entry, { + transform: brfs, + debug: false, + fullPaths: false, + insertGlobals: true + }).once('update', function(name, src) { + t.equal(src.toString(), expected.toString(), 'matches bundler') + app.close() + }) + }) + + +}) + +function matches(t, src, message) { + vm.runInNewContext(src, { + console: { + log: log + }, + global: {} + }) + + function log(msg) { + t.equal(msg, message, 'the output matches in both cases') + } +} + +// var b = browserify(xtend(watchifyArgs, { +// debug: opt.debug, +// }, opt)) +// entries.forEach(function(entry) { +// entry = entry.split(':')[0] +// b.add(path.resolve(entry)) +// }) + +// b.bundle(function(err, expected) { +// if (err) t.fail(err) +// request.get({ +// uri: uri +// }, function(err, resp, data) { +// if (err) t.fail(err) + +// //make sure what browserify bundles matches what budo bundles +// t.equal(data.toString(), expected.toString(), 'bundles match') + +// //also compare output of running both bundles +// vm.runInNewContext(expected, { +// console: { log: log }, +// global: {} +// }); + +// app.close() +// }) +// }) \ No newline at end of file diff --git a/test/test-bundle.js b/test/test-bundle.js index b4fd35f..9637e15 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -88,10 +88,10 @@ function matches(t, entries, opt) { t.equal(data.toString(), expected.toString(), 'bundles match') //also compare output of running both bundles - vm.runInNewContext(expected, { + vm.runInNewContext(data.toString(), { console: { log: log }, global: {} - }); + }) app.close() }) From 5c76f0dddabca55734e2bac4459f62b7c2c7fe1c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 14:56:56 -0400 Subject: [PATCH 087/302] for faster UX, trigger reload on pending --- index.js | 2 +- lib/budo.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 2608d17..6314477 100644 --- a/index.js +++ b/index.js @@ -70,7 +70,7 @@ module.exports = function(entry, opts) { if (ev === 'change' || ev === 'add') emitter.reload(file) }) - .on('update', function(file) { + .on('pending', function(file) { //bundle.js changes emitter.reload(file) }) diff --git a/lib/budo.js b/lib/budo.js index cb7ef1b..0d83670 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -167,6 +167,7 @@ module.exports = function() { server.update(contents) emitter.emit('update', serveAs, contents) }) + .on('pending', emitter.emit.bind(emitter, 'pending')) .on('error', emitter.emit.bind(emitter, 'error')) } From df700b59cc0d603afedc38acc7473da8926f957c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 18:37:06 -0400 Subject: [PATCH 088/302] using callback to get buffered contents --- index.js | 13 ++++++------- lib/watchify.js | 47 ++++++++++++++++------------------------------- package.json | 1 - 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/index.js b/index.js index f3a7477..9813c95 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,14 @@ var create = require('./lib') //public programmatic API -// passes camel case to browserify -// also supports objects for transform etc +// supports objects in transforms module.exports = create -//CLI API (undocumented, private for now) -// uses watchify/bin/args to parse dash-case -// only expects strings for transform etc -// also prints to stdout by default, and -// uses portfinder after base port is taken +//CLI entry point (undocumented, private for now) +// uses watchify/bin/args +// does not support objects in transforms +// uses portfinding on base port +// does not print to stdout module.exports.cli = function cli(args) { var getport = require('getport') var opts = require('minimist')(args, { diff --git a/lib/watchify.js b/lib/watchify.js index 80b43ac..f6abe9c 100644 --- a/lib/watchify.js +++ b/lib/watchify.js @@ -2,7 +2,6 @@ var log = require('bole')('budo') var createWatchify = require('./create-watchify') var Emitter = require('events/') var debounce = require('debounce') -var concat = require('concat-stream') //Eventually this may split into a watchify-server module module.exports = function(entries, userArgs, opt) { @@ -67,38 +66,24 @@ module.exports = function(entries, userArgs, opt) { return } - var didError = false - - var outStream = concat(function(body) { - contents = body - bundleEnd() - }) - - var wb = watchify.bundle() - wb.on('error', function(err) { - err = String(err) - console.error(err) - didError = true - outStream.end('console.error(' + JSON.stringify(err) + ');') - }) - wb.pipe(outStream) - - outStream.on('error', function(err) { - console.error(err) - update() - }) - - function bundleEnd() { - if (opt.verbose && !didError) { - var delay = Date.now() - time - log.info({ - elapsed: Math.round(delay) + 'ms', - type: 'bundle', - url: opt.serve - }) + watchify.bundle(function(err, src) { + if (err) { + err = String(err) + console.error(err) + contents = new Buffer('console.error(' + JSON.stringify(err) + ');') + } else { + contents = src + if (opt.verbose) { + var delay = Date.now() - time + log.info({ + elapsed: Math.round(delay) + 'ms', + type: 'bundle', + url: opt.serve + }) + } } update() - } + }) } return emitter diff --git a/package.json b/package.json index f69b01b..9905ec8 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "dependencies": { "bole": "^2.0.0", "chokidar": "^1.0.1", - "concat-stream": "^1.4.8", "dargs": "^4.0.0", "debounce": "^1.0.0", "ecstatic": "^0.7.2", From fd383c2cc9d21f737ab9ccffade2ebfa327a3f25 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 19:13:55 -0400 Subject: [PATCH 089/302] fixing tests --- index.js | 14 ++++++------- lib/budo.js | 4 +++- lib/index.js | 4 ++-- test/test-args.js | 51 +++-------------------------------------------- test/test-live.js | 12 ++++++----- 5 files changed, 22 insertions(+), 63 deletions(-) diff --git a/index.js b/index.js index 9813c95..8b007d4 100644 --- a/index.js +++ b/index.js @@ -26,20 +26,20 @@ module.exports.cli = function cli(args) { var showHelp = opts.h || opts.help - if (!showHelp && (!entries || entries.filter(Boolean).length === 0)) { - console.error('ERROR:\n no entry scripts specified\n use --help for examples') - process.exit(1) - } - if (showHelp) { - var vers = require('../package.json').version + var vers = require('./package.json').version console.log('budo ' + vers, '\n') - var help = require('path').join(__dirname, 'help.txt') + var help = require('path').join(__dirname, 'bin', 'help.txt') require('fs').createReadStream(help) .pipe(process.stdout) return } + if (!entries || entries.filter(Boolean).length === 0) { + console.error('ERROR:\n no entry scripts specified\n use --help for examples') + process.exit(1) + } + var basePort = opts.port || 9966 getport(basePort, function(err, port) { if (err) { diff --git a/lib/budo.js b/lib/budo.js index 8531fec..45cc9df 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -164,7 +164,9 @@ module.exports = function() { server.update(contents) emitter.emit('update', serveAs, contents) }) - .on('pending', emitter.emit.bind(emitter, 'pending')) + .on('pending', function() { + emitter.emit('pending', serveAs) + }) .on('error', emitter.emit.bind(emitter, 'error')) } diff --git a/lib/index.js b/lib/index.js index d85189c..b64be39 100644 --- a/lib/index.js +++ b/lib/index.js @@ -70,8 +70,8 @@ module.exports = function createBudo(entry, opts, cli) { if (ev === 'change' || ev === 'add') emitter.reload(file) }) - .on('pending', function() { - emitter.reload() + .on('pending', function(file) { + emitter.reload(file) }) } } diff --git a/test/test-args.js b/test/test-args.js index ea43ad4..af9b5f9 100644 --- a/test/test-args.js +++ b/test/test-args.js @@ -3,23 +3,21 @@ var budo = require('../') var browserify = require('browserify') var watchifyArgs = require('watchify').args var path = require('path') -var vm = require('vm') var brfs = require('brfs') var xtend = require('xtend') var entry = 'test/fixtures/app-brfs.js' -test('gets connect info', function(t) { +test('API supports camelCase and transform objects', function(t) { t.plan(1) t.timeoutAfter(10000) var bundler = browserify(xtend(watchifyArgs, { - dbeug: false, + debug: false, transform: brfs, fullPaths: false, insertGlobals: true })) - bundler.add(path.resolve(entry)) bundler.bundle(function(err, expected) { @@ -35,47 +33,4 @@ test('gets connect info', function(t) { app.close() }) }) - - -}) - -function matches(t, src, message) { - vm.runInNewContext(src, { - console: { - log: log - }, - global: {} - }) - - function log(msg) { - t.equal(msg, message, 'the output matches in both cases') - } -} - -// var b = browserify(xtend(watchifyArgs, { -// debug: opt.debug, -// }, opt)) -// entries.forEach(function(entry) { -// entry = entry.split(':')[0] -// b.add(path.resolve(entry)) -// }) - -// b.bundle(function(err, expected) { -// if (err) t.fail(err) -// request.get({ -// uri: uri -// }, function(err, resp, data) { -// if (err) t.fail(err) - -// //make sure what browserify bundles matches what budo bundles -// t.equal(data.toString(), expected.toString(), 'bundles match') - -// //also compare output of running both bundles -// vm.runInNewContext(expected, { -// console: { log: log }, -// global: {} -// }); - -// app.close() -// }) -// }) \ No newline at end of file +}) \ No newline at end of file diff --git a/test/test-live.js b/test/test-live.js index eecb9c8..5455a96 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -73,7 +73,7 @@ test('manual LiveReload triggering', function(t) { test('should not inject LiveReload snippet', function(t) { - t.plan(1) + t.plan(2) t.timeoutAfter(10000) var entry = path.join(__dirname, 'fixtures', 'app.js') @@ -87,20 +87,22 @@ test('should not inject LiveReload snippet', function(t) { t.fail(err) }) .on('connect', function(ev) { - // matchesHTML(t, ev.uri, getHTMLNoLive()) - // setTimeout(function() { + matchesHTML(t, ev.uri, getHTMLNoLive(), function() { app.close() - // }, 100) + }) }) .on('exit', function() { t.ok(true, 'closing') }) }) -function matchesHTML(t, uri, html) { +function matchesHTML(t, uri, html, cb) { request.get({ uri: uri + 'index.html' }, function(err, resp, body) { if (err) t.fail(err) t.equal(body, html || getHTML(), 'matches expected HTML') + + if (cb) + cb() }) } From b034e9071a904d3945ed9ffce3432b2070764f4c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 19:48:58 -0400 Subject: [PATCH 090/302] fixing live options if specified after construction --- docs/programmatic-usage.md | 2 +- index.js | 7 +++++-- lib/budo.js | 23 +++++++++++++---------- lib/server.js | 16 ++++++++-------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md index ebb8449..1fb230a 100644 --- a/docs/programmatic-usage.md +++ b/docs/programmatic-usage.md @@ -40,7 +40,7 @@ Called once the budo server connects. The callback is passed an `event` object t serve: 'bundle/entry%20file.js' //the URL path for our entry file dir: 'app', //the working directory being served host: 'localhost', //defaults to localhost - port: 9966, //the port we're running on + port: 9966 //the port we're running on } ``` diff --git a/index.js b/index.js index 8b007d4..739c42b 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,14 @@ var create = require('./lib') //public programmatic API +// expects args to be camelCase // supports objects in transforms -module.exports = create +module.exports = function budo(entry, opt) { + return create(entry, opt, false) +} //CLI entry point (undocumented, private for now) -// uses watchify/bin/args +// uses watchify/bin/args to arg parse // does not support objects in transforms // uses portfinding on base port // does not print to stdout diff --git a/lib/budo.js b/lib/budo.js index 45cc9df..fbe6bb0 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -40,6 +40,7 @@ module.exports = function() { var hostname = (opt.host || 'localhost') var uri = "http://" + hostname + ":" + opt.port + "/" + log.info("Server running at", uri) //defaults for watch() function @@ -55,9 +56,9 @@ module.exports = function() { } //and default live options defaultLiveOpts = { - plugin: opt['live-plugin'], + plugin: opt.livePlugin || opt['live-plugin'], host: opt.host, - port: emitter['live-port'] + port: opt.livePort || opt['live-port'] } //start watchify process @@ -79,8 +80,7 @@ module.exports = function() { host: hostname, serve: opt.serve, entries: entries, - dir: opt.dir, - 'live-port': opt['live-port'] + dir: opt.dir }) }) return emitter @@ -105,15 +105,18 @@ module.exports = function() { } //enable live-reload capabilities - emitter.live = function(liveOpt) { + emitter.live = function(liveOpts) { if (!started) { - deferreds.push(emitter.live.bind(null, liveOpt)) + deferreds.push(emitter.live.bind(null, liveOpts)) } else { - liveOpt = liveOpt||{} - server._live = !liveOpt.plugin - tinylr = createTinylr(xtend(defaultLiveOpts, liveOpt)) + liveOpts = xtend(defaultLiveOpts, liveOpts) + + //if plugin, ignore live reload in HTML, + //otherwise inject with new options (host/port) + server._live = liveOpts.plugin ? null : liveOpts + + tinylr = createTinylr(liveOpts) emitter.reload = function(file) { - file = file tinylr.reload(file) emitter.emit('reload', file) } diff --git a/lib/server.js b/lib/server.js index 220a3bf..bf27acf 100644 --- a/lib/server.js +++ b/lib/server.js @@ -41,17 +41,17 @@ function createHandler(opts) { var staticHandler = ecstatic(basedir) var router = Router() + var defaulLiveOpts = { + host: opts.host, + port: opts.livePort || opts['live-port'] + } + var emitter = new Emitter() - emitter.live = opts.live + emitter.live = opts.live ? defaulLiveOpts : null emitter.router = router emitter.pending = false emitter.contents = '' - var liveOpts = { - host: opts.host, - port: opts['live-port'] - } - router.addRoute('/' + url.parse(opts.serve).pathname, function(req, res) { log.info({ url: req.url, @@ -80,7 +80,7 @@ function createHandler(opts) { return function(req, res) { //inject LiveReload into HTML content if needed if (html && emitter.live) - res = inject(res, liveOpts) + res = inject(res, emitter.live) log.info({ url: req.url, type: 'static' @@ -98,7 +98,7 @@ function createHandler(opts) { fs.exists(path.join(basedir, 'index.html'), function(exists) { //inject LiveReload into HTML content if needed if (emitter.live) - res = inject(res, liveOpts) + res = inject(res, emitter.live) var type = exists ? 'static' : 'generated' log.info({ From b2f74f3a2c9d65b7c46a0623f55581be4ab4ffcd Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 11 Apr 2015 19:52:54 -0400 Subject: [PATCH 091/302] code commenting --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 739c42b..a79f92c 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ module.exports = function budo(entry, opt) { // uses watchify/bin/args to arg parse // does not support objects in transforms // uses portfinding on base port -// does not print to stdout +// prints to stdout module.exports.cli = function cli(args) { var getport = require('getport') var opts = require('minimist')(args, { From 8f25d899c4ec9ebd45cd2894b392bf11b220791f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 12 Apr 2015 14:19:53 -0400 Subject: [PATCH 092/302] adding test for CLI and API args, and whitespace serving --- package.json | 4 ++- test/test-args.js | 73 ++++++++++++++++++++++++++++++++++++++------- test/test-bundle.js | 3 +- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 9905ec8..a6e8162 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,11 @@ "request": "^2.53.0", "tap-spec": "^3.0.0", "tape": "^4.0.0", + "tree-kill": "0.0.6", "uglify-js": "^2.4.19", "vm": "0.0.1", - "watchify": "^3.1.0" + "watchify": "^3.1.0", + "win-spawn": "^2.0.0" }, "scripts": { "test": "tape test/test*.js | tap-spec", diff --git a/test/test-args.js b/test/test-args.js index af9b5f9..48a304e 100644 --- a/test/test-args.js +++ b/test/test-args.js @@ -7,22 +7,61 @@ var brfs = require('brfs') var xtend = require('xtend') var entry = 'test/fixtures/app-brfs.js' +var spawn = require('win-spawn') -test('API supports camelCase and transform objects', function(t) { +var basedir = path.resolve(__dirname, '..') +var cliPath = path.resolve(basedir, 'bin', 'cmd.js') +var request = require('request') +var kill = require('tree-kill') + +test('CLI works as expected', function(t) { t.plan(1) t.timeoutAfter(10000) - var bundler = browserify(xtend(watchifyArgs, { - debug: false, - transform: brfs, - fullPaths: false, - insertGlobals: true - })) - bundler.add(path.resolve(entry)) - - bundler.bundle(function(err, expected) { + doBundle(function(err, expected) { if (err) return t.fail(err) + var args = [entry, '-v', '-t', 'brfs', '--no-debug', '--insert-globals'] + + var uri = 'http://localhost:9966/' + var proc = spawn(cliPath, args, { cwd: basedir, env: process.env }) + proc.stderr.on('data', bail) + proc.stdout.on('data', function(data) { + try { data = JSON.parse(data) } + catch (e) {} + //get correct port + if (data && data.message) { + var msg = 'server running at ' + var idx = data.message.toLowerCase().indexOf(msg) + if (idx >= 0) { + uri = data.message.slice(idx+msg.length) + } + } + //bundle ready + else if (data && data.type === 'bundle') { + request.get({ uri: uri + entry }, function(err, res, body) { + if (err) return bail(err) + t.equal(body.toString(), expected.toString(), 'bundles match') + kill(proc.pid) + }) + } + }) + + function bail(err) { + proc.on('exit', function() { + t.fail(err) + }) + kill(proc.pid) + } + }) +}) + +test('API supports camelCase and transform objects', function(t) { + t.plan(1) + t.timeoutAfter(10000) + + doBundle(function(err, expected) { + if (err) return t.fail(err) var app = budo(entry, { transform: brfs, debug: false, @@ -33,4 +72,16 @@ test('API supports camelCase and transform objects', function(t) { app.close() }) }) -}) \ No newline at end of file +}) + + +function doBundle(cb) { + var bundler = browserify(xtend(watchifyArgs, { + debug: false, + transform: brfs, + fullPaths: false, + insertGlobals: true + })) + bundler.add(path.resolve(entry)) + bundler.bundle(cb) +} \ No newline at end of file diff --git a/test/test-bundle.js b/test/test-bundle.js index 9637e15..95f8ecd 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -39,7 +39,8 @@ test('serves with spaces and entry to bundle.js', run('test/fixtures/with space. })) test('serves with spaces default', run('test/fixtures/with space.js', { - message: 'with space' + message: 'with space', + shouldServe: 'test/fixtures/with%20space.js' })) function run(entries, opt) { From 065eecd378618166caa098377fbd35c29b59607a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 12 Apr 2015 14:58:17 -0400 Subject: [PATCH 093/302] clean up the way debug is handled --- index.js | 4 ++-- lib/create-watchify.js | 16 ++++++++++++---- lib/index.js | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index a79f92c..5899a00 100644 --- a/index.js +++ b/index.js @@ -15,8 +15,8 @@ module.exports = function budo(entry, opt) { module.exports.cli = function cli(args) { var getport = require('getport') var opts = require('minimist')(args, { - boolean: ['stream', 'debug'], - default: { stream: true, debug: true } + boolean: ['stream'], + default: { stream: true } }) //user can silent budo with --no-stream diff --git a/lib/create-watchify.js b/lib/create-watchify.js index 0483e05..dfc6820 100644 --- a/lib/create-watchify.js +++ b/lib/create-watchify.js @@ -61,16 +61,24 @@ function getDefaultArgs(opt) { //ensure we have watchify args setup opt = assign({ cache: {}, packageCache: {} }, opt) - //disable delay since we will handle debouncing manually + //disable watchify delay since we will handle debouncing manually opt.delay = 0 + //handle debug flags + var debug = typeof opt.debug === 'undefined' ? opt.d : opt.debug + + //if minimist didn't parse it as a boolean (since it can be a string too) + if (debug === 'false') + debug = false + //enable debug by default - if (opt.d !== false && opt.debug !== false) { + if (debug !== false) { delete opt.d - opt.debug = true + //allow string like 'eval' + opt.debug = debug === 'true' ? true : debug } //if user explicitly disabled debug... - else if (opt.d === false || opt.debug === false) { + else if (debug === false) { delete opt.d delete opt.debug } diff --git a/lib/index.js b/lib/index.js index b64be39..fcb9204 100644 --- a/lib/index.js +++ b/lib/index.js @@ -38,7 +38,7 @@ module.exports = function createBudo(entry, opts, cli) { return map.from }) - //if user specified -o use that as our entry map + //if user specified --serve use that as our entry map var serveAs = argv.serve if (serveAs && typeof serveAs === 'string') file = serveAs From 59eda97c207e3ea4076fd18749a413d2bab7c666 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 12 Apr 2015 15:00:43 -0400 Subject: [PATCH 094/302] multiple entry docs --- docs/basics.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index 1697020..a97aa6e 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -72,4 +72,12 @@ budo index.js --live | garnish Now when you save the `index.js` file, it will trigger a LiveReload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. -Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). \ No newline at end of file +Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). + +## multiple entries + +Budo also supports multiple entry points; they will all get concatenated into a single bundle. The entry point for your ` ``` -You can specify a different end point for the serve like so: +You can specify a different end point for the server with a colon: ```sh budo index.js:static/bundle.js | garnish @@ -76,8 +76,8 @@ Alternatively, you can use `--live-plugin` if you want to enable LiveReload thro ## multiple entries -Budo also supports multiple entry points; they will all get concatenated into a single bundle. The entry point for your ` ``` -You can specify a different end point for the server with a colon: +If you want to use relative or absolute paths, it's recommended to specify a "server URL." You can do this with a colon separator: ```sh -budo index.js:static/bundle.js | garnish +budo /proj/foo/index.js:static/bundle.js | garnish ``` -Your HTML would the look like this: +Now, you can use the following as your HTML: ```html ``` +Also see the [`--serve` option](#multiple entries). + ## local installation If you are using these in your modules for demos/etc, you should save them locally so that others can get the same versions when they `git clone` and `npm install` your repo. diff --git a/lib/get-server-path.js b/lib/get-server-path.js index 1545552..d486668 100644 --- a/lib/get-server-path.js +++ b/lib/get-server-path.js @@ -1,11 +1,9 @@ var url = require('url') +var path = require('path') var defaultName = 'bundle.js' var fixable = /^.[\\\/]+/ -// tries to use user path, -// otherwise uses "bundle.js" - // This could be improved to create better // consistency with relative/absolute/etc paths // PRs/ideas welcome :) @@ -24,9 +22,9 @@ module.exports = function cleanPath (file, serveAs) { file = file.replace(fixable, '') } else if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/.test(file)) { // otherwise, see if path is relative/absolute - return defaultName + return path.basename(file) } - // path is not relative, just treat as usual + // path is not relative/absolute, just treat as usual return url.parse(file).path } diff --git a/test/test-bundle.js b/test/test-bundle.js index c434223..9c92613 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -34,13 +34,17 @@ test('handles relative paths', run(['./test/fixtures/app.js'], { test('fallback for relative paths', run(['../budo/test/fixtures/app.js'], { message: 'from browserify', - shouldServe: 'bundle.js' + shouldServe: 'app.js' })) var abs = path.resolve(__dirname, 'fixtures', 'app.js') test('fallback for relative paths', run(abs, { message: 'from browserify', - shouldServe: 'bundle.js' + shouldServe: 'app.js' +})) +test('fallback for relative paths', run(abs + ':boop.js', { + message: 'from browserify', + shouldServe: 'boop.js' })) test('does not break on query params', run('test/fixtures/with space.js:bundle.js?debug=true', { From 2c24d27fba2d90cf3dbebd5ac0ee4afee3c767ad Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 23 Jun 2015 16:03:35 -0400 Subject: [PATCH 127/302] wording --- docs/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index 89ef448..23ec9d2 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -28,7 +28,7 @@ The `src` for your script tag should match the entry point you gave. ``` -If you want to use relative or absolute paths, it's recommended to specify a "server URL." You can do this with a colon separator: +You can specify a different end point for the server with a colon. This is useful for relative and absolute paths, for example: ```sh budo /proj/foo/index.js:static/bundle.js | garnish From e16701854e0ffd38c0ac83fcf9f523df9d1c8c64 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 23 Jun 2015 16:03:39 -0400 Subject: [PATCH 128/302] 4.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2e3e0d..71e28d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "4.0.0", + "version": "4.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 853d57b131ad43e740a771b0d2c84aa4890f3cc8 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 24 Jun 2015 21:52:59 -0400 Subject: [PATCH 129/302] updating readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5f81ac..4191b91 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ See [API usage](docs/programmatic-usage.md) for more details. [(click for demo)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) -The original motivation for making budō was to build a simple tool around Chrome Script Injection. This has since split off into its own repository: [budo-chrome](https://github.com/mattdesl/budo-chrome) to minimize the scope of budō. +The original motivation for making budō was to build an *experimental* tool and proof-of-concept around Chrome Script Injection. This has since split off into its own repository: [budo-chrome](https://github.com/mattdesl/budo-chrome). ## License From ee91d4ca8ad591f62ea21a98f9a1eb0415ac838e Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Wed, 5 Aug 2015 12:29:20 -0700 Subject: [PATCH 130/302] Always serve index page for --pushstate For applications that utilize HTML5 pushstate to mange the URL, it's desirable for the server to always return the index.html page instead of a 404. Passing `--pushstate` on the command line will return the index.html if no other resource matches the path. Issue: #41 --- bin/help.txt | 1 + lib/server.js | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/bin/help.txt b/bin/help.txt index 84cee06..3da6eb8 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -10,6 +10,7 @@ Options: --live enable LiveReload integration --live-plugin enable LiveReload but do not inject script tag --live-port the LiveReload port, default 35729 + --pushstate always render the index page instead of a 404 page --verbose, -v verbose timing information for re-bundles --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout diff --git a/lib/server.js b/lib/server.js index 1041b0e..260aa68 100644 --- a/lib/server.js +++ b/lib/server.js @@ -76,6 +76,19 @@ function createHandler(opts) { return emitter + function pushstate(req, res) { + staticHandler(req, res, function () { + if (opts.pushstate) { + // reset for home + req.url = '/' + res.statusCode = 200 + home(req, res) + } else { + res.end() + } + }) + } + function wildcard(html) { return function(req, res) { //inject LiveReload into HTML content if needed @@ -85,7 +98,7 @@ function createHandler(opts) { url: req.url, type: 'static' }) - staticHandler(req, res) + pushstate(req, res) } } From 344b5b9967c1735ca3f020a6ddf59725890ba3a3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 21 Aug 2015 08:18:21 -0400 Subject: [PATCH 131/302] 4.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71e28d0..9e027a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "4.1.0", + "version": "4.2.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 39fdb4f3a2e0247e054ca01e00f0800dc202a3d9 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 21 Aug 2015 10:04:36 -0400 Subject: [PATCH 132/302] document push state option --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4191b91..ed43e35 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Options: --live enable LiveReload integration --live-plugin enable LiveReload but do not inject script tag --live-port the LiveReload port, default 35729 + --pushstate always render the index page instead of a 404 page --verbose, -v verbose timing information for re-bundles --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout From a6ba33b3d31057f3d0a3d5107c1c507bfa18d0a0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 21 Aug 2015 10:04:41 -0400 Subject: [PATCH 133/302] 4.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e027a0..eeab3b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "4.2.0", + "version": "4.2.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 0cdd39753294286c245acd9c4d6e81b15a21c453 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 21 Aug 2015 14:28:56 -0400 Subject: [PATCH 134/302] updating readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed43e35..be8f5d9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ See [docs](#docs) for more features. PRs/suggestions/comments welcome. - [error reporting](docs/errors.md) - [running tests and examples](docs/tests-and-examples.md) - [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) -- [rapid prototyping with budō and wzrd](http://mattdesl.svbtle.com/rapid-prototyping) +- [rapid prototyping with budō](http://mattdesl.svbtle.com/rapid-prototyping) ## usage From f69d889a5e8e5cdfaafa48a892d49bbe95f50b35 Mon Sep 17 00:00:00 2001 From: Chris Van Date: Wed, 2 Sep 2015 17:42:20 -0700 Subject: [PATCH 135/302] fix typos in Programmatic Usage doc --- docs/programmatic-usage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md index 0721cb9..21746c4 100644 --- a/docs/programmatic-usage.md +++ b/docs/programmatic-usage.md @@ -56,11 +56,11 @@ Where `urlPath` is the served name (e.g. `bundle%20file.js`) and `src` is the ne #### `b.on('reload')` -If live reload is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. +If live reload is enabled (i.e. through `live` or `live-plugin`), this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. #### `b.on('watch')` -If file watching is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after HTML and CSS files have changed. The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. +If file watching is enabled (i.e. through `live` or `live-plugin`), this event will be triggered after HTML and CSS files have changed. The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. #### `b.reload(path)` @@ -120,4 +120,4 @@ Now running `gulp dev` will spin up a server on 9966, spawn watchify, and increm - [gulp](https://github.com/mattdesl/budo-gulp-starter) - [npm scripts](https://gist.github.com/mattdesl/b6990e7c7221c9cc05aa) -- [LiveReactLoad](https://gist.github.com/mattdesl/2aa5b45ed1f230635a04) \ No newline at end of file +- [LiveReactLoad](https://gist.github.com/mattdesl/2aa5b45ed1f230635a04) From 47fca30113bf8ff753e3cf78f816c870e368d4fc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 5 Sep 2015 15:05:40 -0400 Subject: [PATCH 136/302] big refactoring and fixes in progress --- bin/help.txt | 25 +++---- example/simple.js | 2 + index.js | 67 +++++++++++++------ lib/budo.js | 2 +- lib/create-budo.js | 148 ++++++++++++++++++++++++++++++++++++++++++ lib/create-bundler.js | 34 ++++++++++ lib/create-server.js | 73 +++++++++++++++++++++ lib/get-ports.js | 33 ++++++++++ lib/map-entry.js | 19 ++++++ lib/parse-args.js | 36 ++++++++++ package.json | 11 +++- test/test-next.js | 44 +++++++++++++ 12 files changed, 459 insertions(+), 35 deletions(-) create mode 100644 example/simple.js create mode 100644 lib/create-budo.js create mode 100644 lib/create-bundler.js create mode 100644 lib/create-server.js create mode 100644 lib/get-ports.js create mode 100644 lib/map-entry.js create mode 100644 lib/parse-args.js create mode 100644 test/test-next.js diff --git a/bin/help.txt b/bin/help.txt index 3da6eb8..f7028a2 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -1,25 +1,26 @@ Usage: - budo index.js [opts] [browserify opts] + budo index.js [opts] -- [browserify opts] Options: --help, -h show help message - --port the port to run, default 9966 - --host the host, default "localhost" - --dir the directory to serve, and the base for --outfile - --serve override the bundle path being served - --live enable LiveReload integration - --live-plugin enable LiveReload but do not inject script tag - --live-port the LiveReload port, default 35729 - --pushstate always render the index page instead of a 404 page - --verbose, -v verbose timing information for re-bundles + --version show version + --port, -p the port to run, default 9966 + --host, -H the host, default "localhost" + --dir, -d a path, or array of paths for base static content + --serve, -s override the bundle path being served + --live, -l enable LiveReload integration + --live-port, -L the LiveReload port, default 35729 + --pushstate, -P always render the index page instead of a 404 page --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout --no-debug do not use inline source maps + --no-portfind will not attempt auto-portfinding -Other Options: +Browserify Options: https://github.com/substack/node-browserify Examples: budo src/index.js --live --dir app - budo index.js --verbose --transform brfs + budo index.js -- --transform brfs budo index.js:bundle.js --port 8000 + budo src/ --paths app --paths \ No newline at end of file diff --git a/example/simple.js b/example/simple.js new file mode 100644 index 0000000..996ce85 --- /dev/null +++ b/example/simple.js @@ -0,0 +1,2 @@ +var res = require('url').parse(window.location.href) +console.log(res) diff --git a/index.js b/index.js index 69964ba..60c4c37 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,51 @@ -var create = require('./lib') +var parseArgs = require('./lib/parse-args') +var budo = require('./lib/create-budo') +var color = require('term-color') -//public programmatic API -// expects args to be camelCase -// supports objects in transforms -module.exports = function budo(entry, opt) { - return create(entry, opt, false) +module.exports = budo +module.exports.cli = budoCLI + +function budoCLI (args, opts) { + var argv = parseArgs(args, opts) + + if (argv.stream !== false) { + argv.stream = process.stdout + } + + var entries = argv._ + delete argv._ + + argv.browserifyArgs = argv['--'] + delete argv['--'] + + if (argv.version) { + console.log(require('./package.json').version) + return null + } + + if (argv.help) { + var help = require('path').join(__dirname, 'bin', 'help.txt') + require('fs').createReadStream(help) + .pipe(process.stdout) + return null + } + + if (argv.o || argv.outfile) { + console.error(color.yellow('WARNING'), '--outfile has been removed in budo@3.0') + // ensure we don't pass to watchify + delete argv.o + delete argv.outfile + } + + return budo(entries, argv).on('error', exit) } -//CLI entry point (undocumented, private for now) -// uses watchify/bin/args to arg parse -// does not support objects in transforms -// uses portfinding on base port -// prints to stdout +function exit (err) { + console.log(color.red('ERROR'), err.message) + process.exit(1) +} + +/* module.exports.cli = function cli(args) { var getport = require('getport') var opts = require('minimist')(args, { @@ -68,13 +102,4 @@ module.exports.cli = function cli(args) { }) }) } - - -function isSubargError(args) { - var end = args.indexOf('--') - if (end === -1) - end = Number.MAX_VALUE - var sub1 = args.indexOf('[') - var sub2 = args.indexOf(']') - return (sub1 >= 0 && sub1 < end) || (sub2 >= 0 && sub2 < end) -} \ No newline at end of file +*/ diff --git a/lib/budo.js b/lib/budo.js index b252591..22a493d 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -36,7 +36,7 @@ module.exports = function() { var hostname = (opt.host || 'localhost') var uri = "http://" + hostname + ":" + opt.port + "/" - log.info({ message: "Server running at " + uri, type: 'connect', url: uri }) + log.info({ message: "Server running at " + uri, type: 'connect' }) //defaults for watch() function defaultGlobs = ['**/*.{html,css}'] diff --git a/lib/create-budo.js b/lib/create-budo.js new file mode 100644 index 0000000..6aa62cf --- /dev/null +++ b/lib/create-budo.js @@ -0,0 +1,148 @@ +var bole = require('bole') +var log = bole('budo') +var xtend = require('xtend') +var once = require('once') +var EventEmitter = require('events').EventEmitter + +var getPorts = require('./get-ports') +var createServer = require('./create-server') +var createBundler = require('./create-bundler') +var mapEntry = require('./map-entry') + +module.exports = createBudo +function createBudo (entries, opts) { + // if no entries are specified + if (entries && !Array.isArray(entries) && typeof entries === 'object') { + opts = entries + entries = [] + } + + // do not mutate user options + opts = xtend({ portfind: true }, opts) + + // perhaps later this will be configurable + opts.cwd = process.cwd() + + // log to output stream + if (opts.stream) { + bole.output({ + stream: opts.stream, + level: 'debug' + }) + } + + // optionally allow as arrays + entries = [].concat(entries).filter(Boolean) + + var entryObjects = entries.map(mapEntry) + var entryFiles = entryObjects.map(function (entry) { + return entry.from + }) + + if (opts.serve && typeof opts.serve !== 'string') { + throw new TypeError('opts.serve must be a string or undefined') + } else if (!opts.serve && entries.length > 0) { + opts.serve = entryObjects[0].url + } + + // default to cwd + if (!opts.dir || opts.dir.length === 0) { + opts.dir = opts.cwd + } + + var hostname = (opts.host || 'localhost') + var emitter = new EventEmitter() + var watcher, middleware + + if (entries.length === 0) { + return bail('no entry files specified!') + } + + watcher = createBundler(entryFiles, opts) + watcher.on('log', function (ev) { + if (ev.type === 'bundle') { + ev.url = '/' + opts.serve + ev.elapsed = ev.elapsed + 'ms' + } + log.info(ev) + }) + + watcher.on('update', function (contents, deps) { + emitter.emit('update', contents, deps) + }) + + middleware = watcher.middleware + + var server = createServer(middleware, opts) + var closed = false + var running = false + + // public API + emitter.close = once(close) + + // start portfinding + connect + getPorts(opts, handlePorts) + + return emitter + + function handlePorts (err, result) { + if (closed) return + if (err) { + emitter.emit('error', err) + return + } + + opts.port = result.port + + // only override if we actually needed to find livePort + if (typeof result.livePort !== 'undefined') { + opts.livePort = result.livePort + } + + // improve error messaging + server.on('error', function (err) { + if (err.code === 'EADDRINUSE') { + err.message = 'port ' + opts.port + ' is in use' + emitter.emit('error', err) + } else { + emitter.emit('error', err) + } + }) + + // start server + server.listen(opts.port, opts.host, connect) + } + + function connect () { + if (closed) return + running = true + + var port = opts.port + var uri = 'http://' + hostname + ':' + port + '/' + + log.info({ message: 'Server running at', url: uri, type: 'connect' }) + + // provide info on server connection + emitter.emit('connect', { + uri: uri, + port: port, + host: hostname, + serve: opts.serve, + entries: entries, + static: opts.static, + dir: opts.dir + }) + } + + function close () { + closed = true + if (running) server.close() + } + + function bail (msg) { + process.nextTick(function () { + emitter.emit('error', new Error(msg)) + }) + return emitter + } +} diff --git a/lib/create-bundler.js b/lib/create-bundler.js new file mode 100644 index 0000000..76c6cee --- /dev/null +++ b/lib/create-bundler.js @@ -0,0 +1,34 @@ +var xtend = require('xtend') +var createMiddleware = require('watchify-middleware').emitter +var fromArgs = require('browserify/bin/args') +var browserify = require('browserify') +var path = require('path') + +module.exports = createBundler +function createBundler (files, opts) { + var bOpts = xtend({ + cache: {}, + packageCache: {}, + debug: opts.debug + }, opts.browserify) + + var bundler + var args = opts.browserifyArgs + // CLI args for browserify + if (args && Array.isArray(args)) { + bundler = fromArgs(args, bOpts) + } + // just assume JS only options + else { + bundler = browserify(bOpts) + } + + files.forEach(function (file) { + bundler.add(path.resolve(file)) + }) + + return createMiddleware(bundler, { + delay: opts.delay, + errorHandler: true + }) +} diff --git a/lib/create-server.js b/lib/create-server.js new file mode 100644 index 0000000..00dc70d --- /dev/null +++ b/lib/create-server.js @@ -0,0 +1,73 @@ +var http = require('http') +var path = require('path') +var defaultIndex = require('simple-html-index') +var ecstatic = require('ecstatic') +var Router = require('routes-router') +var inject = require('inject-lr-script') +var fs = require('fs') +var log = require('bole')('budo') +var url = require('url') + +module.exports = createServer +function createServer (middleware, opts) { + var router = Router() + var staticPaths = opts.dir + var basedir = staticPaths[0] || process.cwd() + var staticHandler = ecstatic(basedir) + + var server = http.createServer(router) + var live = opts.live + + router.addRoute('/index.html', home) + router.addRoute('/', home) + // router.addRoute('*.html', wildcard(true)) + // router.addRoute('*', wildcard()) + + if (middleware) { + router.addRoute('/' + opts.serve, function (req, res) { + log.info({ + url: req.url, + type: 'generated' + }) + middleware(req, res) + }) + } + + // allow user to toggle live reload integration + server.setLive = setLive + return server + + function setLive (liveOpts) { + live = liveOpts + } + + function home (req, res) { + fs.exists(path.join(basedir, 'index.html'), function (exists) { + // inject LiveReload into HTML content if needed + if (live) + res = inject(res, live) + + var type = exists ? 'static' : 'generated' + log.info({ + url: req.url, + type: type + }) + + if (exists) { + staticHandler(req, res) + } else { + generateIndex(opts.serve, req, res) + } + }) + } + + function generateIndex (outfile, req, res) { + res.setHeader('content-type', 'text/html') + + var stream = opts.defaultIndex || defaultIndex + stream({ + entry: outfile, + title: 'budo' + }).pipe(res) + } +} diff --git a/lib/get-ports.js b/lib/get-ports.js new file mode 100644 index 0000000..f8fce56 --- /dev/null +++ b/lib/get-ports.js @@ -0,0 +1,33 @@ +var getport = require('getport') +var xtend = require('xtend') +var each = require('async-each') + +module.exports = getServerPorts +function getServerPorts (opt, cb) { + opt = xtend({ port: 9966, livePort: 35729 }, opt) + + // try to use exact port specified or the defaults + if (!opt.portfind) { + return process.nextTick(function () { + cb(null, { + port: opt.port, + livePort: opt.livePort + }) + }) + } + + var tasks = [ opt.port ] + if (opt.live) { + tasks.push(opt.livePort) + } + + each(tasks, function (base, next) { + getport(base, function (err, port) { + if (err) return next(new Error('no available ports after ' + base)) + next(null, port) + }) + }, function (err, ports) { + if (err) return cb(err) + cb(null, { port: ports[0], livePort: ports[1]}) + }) +} diff --git a/lib/map-entry.js b/lib/map-entry.js new file mode 100644 index 0000000..363c929 --- /dev/null +++ b/lib/map-entry.js @@ -0,0 +1,19 @@ +var path = require('path') +var url = require('url') + +module.exports = mapEntry +function mapEntry (file) { + var parts = file.split(':') + var entry = {} + + if (parts.length > 1 && parts[1].length > 0) { + entry.from = parts[0] + entry.to = parts[1] + } else { + entry.from = entry.to = file + } + + var filepath = path.basename(entry.from) + entry.url = url.parse(filepath).pathname + return entry +} diff --git a/lib/parse-args.js b/lib/parse-args.js new file mode 100644 index 0000000..7bd662c --- /dev/null +++ b/lib/parse-args.js @@ -0,0 +1,36 @@ +var minimist = require('minimist') +var xtend = require('xtend') + +module.exports = parseArgs +function parseArgs (args, opt) { + var argv = minimist(args, { + boolean: [ + 'stream', + 'debug', + 'verbose', + 'live', + 'portfind' + ], + string: [ + 'host', + 'port' + ], + default: { + port: 9966, + debug: true, + stream: true, + portfind: true + }, + alias: { + port: 'p', + help: 'h', + host: 'H', + dir: 'd', + live: 'l', + 'live-port': 'L', + pushstate: 'P' + }, + '--': true + }) + return xtend(argv, opt) +} diff --git a/package.json b/package.json index eeab3b6..01f2e0e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,10 @@ "url": "https://github.com/mattdesl" }, "dependencies": { + "array-find": "^1.0.0", + "async-each": "^0.1.6", "bole": "^2.0.0", + "browserify": "^11.0.1", "chokidar": "^1.0.1", "concat-stream": "^1.4.8", "dargs": "^4.0.0", @@ -23,11 +26,17 @@ "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "minimist": "^1.1.0", + "once": "^1.3.2", + "path-is-absolute": "^1.0.0", + "patterns": "^1.0.2", "resolve": "^1.1.6", "routes-router": "^4.1.2", + "simple-html-index": "^1.1.0", + "term-color": "^1.0.1", "through2": "^0.6.3", "tiny-lr": "^0.1.5", - "watchify": "^3.1.2", + "watchify": "^3.3.1", + "watchify-middleware": "^1.0.0", "xtend": "^4.0.0" }, "devDependencies": { diff --git a/test/test-next.js b/test/test-next.js new file mode 100644 index 0000000..e674b90 --- /dev/null +++ b/test/test-next.js @@ -0,0 +1,44 @@ +var test = require('tape') +var budo = require('../') +var http = require('http') + +test('user can disable portfinding', function (t) { + t.plan(1) + var server = http.createServer().listen(9966, function () { + var b = budo({ + port: 9966, + portfind: false + }) + b.on('error', function (err) { + t.equal(err.code, 'EADDRINUSE') + b.close() + server.close() + }) + }) +}) + +test('portfinds by default', function (t) { + t.plan(1) + var server = http.createServer().listen(9966, function () { + var b = budo({ + port: 9966 + }) + b.on('error', t.fail) + b.on('connect', function (ev) { + t.equal(ev.port, 9967) + b.close() + server.close() + }) + }) +}) + +test('gets connect', function (t) { + t.plan(1) + var b = budo({ + port: 9966, + portfind: false + }) + b.on('connect', function (ev) { + t.equal(ev.port, 9966) + }) +}) From 8e90142e028d3d50252ff8aeca8361ff1d53f355 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 5 Sep 2015 15:27:49 -0400 Subject: [PATCH 137/302] some more refactors --- .gitignore | 3 ++- .npmignore | 1 + lib/create-budo.js | 6 ++--- lib/create-server.js | 58 +++++++++++++++++++++++++++++++++++++------- lib/default-index.js | 14 ----------- lib/file-watch.js | 23 ++++++++---------- package.json | 2 ++ 7 files changed, 66 insertions(+), 41 deletions(-) delete mode 100644 lib/default-index.js diff --git a/.gitignore b/.gitignore index 1cd3404..46180f5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules *.log .DS_Store bundle.js -bundle-expected.js \ No newline at end of file +bundle-expected.js +.tmp \ No newline at end of file diff --git a/.npmignore b/.npmignore index 1663f92..ec15756 100644 --- a/.npmignore +++ b/.npmignore @@ -9,4 +9,5 @@ demo/ docs/ example/ .npmignore +.tmp LICENSE.md \ No newline at end of file diff --git a/lib/create-budo.js b/lib/create-budo.js index 6aa62cf..ba1af2e 100644 --- a/lib/create-budo.js +++ b/lib/create-budo.js @@ -52,7 +52,7 @@ function createBudo (entries, opts) { var hostname = (opts.host || 'localhost') var emitter = new EventEmitter() - var watcher, middleware + var watcher if (entries.length === 0) { return bail('no entry files specified!') @@ -71,9 +71,7 @@ function createBudo (entries, opts) { emitter.emit('update', contents, deps) }) - middleware = watcher.middleware - - var server = createServer(middleware, opts) + var server = createServer(watcher.middleware, opts) var closed = false var running = false diff --git a/lib/create-server.js b/lib/create-server.js index 00dc70d..8d4a876 100644 --- a/lib/create-server.js +++ b/lib/create-server.js @@ -6,22 +6,28 @@ var Router = require('routes-router') var inject = require('inject-lr-script') var fs = require('fs') var log = require('bole')('budo') -var url = require('url') module.exports = createServer function createServer (middleware, opts) { var router = Router() - var staticPaths = opts.dir - var basedir = staticPaths[0] || process.cwd() - var staticHandler = ecstatic(basedir) + var staticPaths = [].concat(opts.dir).filter(Boolean) + if (staticPaths.length === 0) { + staticPaths = [ process.cwd() ] + } + var basedir = staticPaths[0] + + var staticHandlers = staticPaths.map(function (filepath, i, list) { + var last = i === list.length - 1 + return ecstatic({ root: filepath, handleError: last }) + }) var server = http.createServer(router) var live = opts.live router.addRoute('/index.html', home) router.addRoute('/', home) - // router.addRoute('*.html', wildcard(true)) - // router.addRoute('*', wildcard()) + router.addRoute('*.html', wildcard(true)) + router.addRoute('*', wildcard()) if (middleware) { router.addRoute('/' + opts.serve, function (req, res) { @@ -41,6 +47,41 @@ function createServer (middleware, opts) { live = liveOpts } + function wildcard (html) { + return function (req, res) { + // inject LiveReload into HTML content if needed + if (html && live) + res = inject(res, live) + log.info({ + url: req.url, + type: 'static' + }) + staticRequest(req, res) + } + } + + function staticRequest (req, res, stack) { + if (!stack) { + stack = staticHandlers.slice() + } + var nextFn = stack.shift() + nextFn(req, res, function () { + if (stack.length === 0) { + if (opts.pushstate) { + // reset for home + req.url = '/' + res.statusCode = 200 + home(req, res) + } else { + res.statusCode = 404 + res.end('404 not found: ' + req.url) + } + } else { + staticRequest(req, res, stack) + } + }) + } + function home (req, res) { fs.exists(path.join(basedir, 'index.html'), function (exists) { // inject LiveReload into HTML content if needed @@ -54,7 +95,7 @@ function createServer (middleware, opts) { }) if (exists) { - staticHandler(req, res) + staticRequest(req, res) } else { generateIndex(opts.serve, req, res) } @@ -66,8 +107,7 @@ function createServer (middleware, opts) { var stream = opts.defaultIndex || defaultIndex stream({ - entry: outfile, - title: 'budo' + entry: outfile }).pipe(res) } } diff --git a/lib/default-index.js b/lib/default-index.js deleted file mode 100644 index bc06cb6..0000000 --- a/lib/default-index.js +++ /dev/null @@ -1,14 +0,0 @@ -var through2 = require('through2') - -module.exports = function(opt) { - var out = through2() - out.end([ - '', - '', - '', - '', - '', - '' - ].join('')) - return out -} \ No newline at end of file diff --git a/lib/file-watch.js b/lib/file-watch.js index 5b9612e..5c15345 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -1,4 +1,4 @@ -//a thin wrapper around chokidar file watching HTML / CSS +// a thin wrapper around chokidar file watching HTML / CSS var log = require('bole')('budo') var watch = require('chokidar').watch var xtend = require('xtend') @@ -10,7 +10,7 @@ var ignores = [ '*.swp', 'thumbs.db', 'desktop.ini' ] -module.exports = function(glob, opt) { +module.exports = function (glob, opt) { opt = xtend({ ignored: ignores, ignoreInitial: true @@ -24,13 +24,12 @@ module.exports = function(glob, opt) { watcher.on('change', onWatch.bind(null, 'change')) // chokidar@1.0.0-r6 only allows close after ready event - watcher.once('ready', function() { + watcher.once('ready', function () { ready = true - if (closed) - watcher.close() + if (closed) watcher.close() }) - - function onWatch(event, path) { + + function onWatch (event, path) { emitter.emit('watch', event, path) log.debug({ type: event, @@ -38,14 +37,12 @@ module.exports = function(glob, opt) { }) } - emitter.close = function() { - if (closed) - return - if (ready) - watcher.close() + emitter.close = function () { + if (closed) return + if (ready) watcher.close() closed = true } return emitter } -module.exports.ignores = ignores \ No newline at end of file +module.exports.ignores = ignores diff --git a/package.json b/package.json index 01f2e0e..7cb2514 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ }, "dependencies": { "array-find": "^1.0.0", + "async": "^1.4.2", "async-each": "^0.1.6", + "async-filter-each": "^1.0.0", "bole": "^2.0.0", "browserify": "^11.0.1", "chokidar": "^1.0.1", From 566d1c0f456ea42e623f74355ac40d8cc262503d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 5 Sep 2015 16:46:29 -0400 Subject: [PATCH 138/302] getting it working with live reload again --- lib/create-budo.js | 103 ++++++++++++++++++++++++++++--- lib/create-bundler.js | 9 ++- lib/create-server.js | 20 +++--- lib/file-watch.js | 3 +- lib/map-entry.js | 19 +++--- lib/parse-args.js | 4 +- lib/server.js | 140 ------------------------------------------ lib/tinylr.js | 34 +++++----- lib/watchify.js | 109 -------------------------------- package.json | 8 +-- 10 files changed, 142 insertions(+), 307 deletions(-) delete mode 100644 lib/server.js delete mode 100644 lib/watchify.js diff --git a/lib/create-budo.js b/lib/create-budo.js index ba1af2e..34a1e1c 100644 --- a/lib/create-budo.js +++ b/lib/create-budo.js @@ -2,11 +2,16 @@ var bole = require('bole') var log = bole('budo') var xtend = require('xtend') var once = require('once') +var path = require('path') var EventEmitter = require('events').EventEmitter +var noop = function () {} var getPorts = require('./get-ports') var createServer = require('./create-server') var createBundler = require('./create-bundler') +var createFileWatch = require('./file-watch') +var createTinylr = require('./tinylr') + var mapEntry = require('./map-entry') module.exports = createBudo @@ -52,14 +57,14 @@ function createBudo (entries, opts) { var hostname = (opts.host || 'localhost') var emitter = new EventEmitter() - var watcher + var bundler if (entries.length === 0) { return bail('no entry files specified!') } - watcher = createBundler(entryFiles, opts) - watcher.on('log', function (ev) { + bundler = createBundler(entryFiles, opts) + bundler.on('log', function (ev) { if (ev.type === 'bundle') { ev.url = '/' + opts.serve ev.elapsed = ev.elapsed + 'ms' @@ -67,22 +72,92 @@ function createBudo (entries, opts) { log.info(ev) }) - watcher.on('update', function (contents, deps) { - emitter.emit('update', contents, deps) - }) + bundler.on('update', emitter.emit.bind(emitter, 'update')) + bundler.on('pending', emitter.emit.bind(emitter, 'pending')) - var server = createServer(watcher.middleware, opts) + var server = createServer(bundler.middleware, opts) var closed = false - var running = false + var started = false + var fileWatcher = null + var tinylr = null + var deferredWatch = noop + var deferredLive = noop // public API emitter.close = once(close) + emitter.reload = reload + emitter.live = live + emitter.watch = watch + + // setup defaults for live reload / watchify + if (opts.live) { + emitter.live() + .on('watch', function (ev, file) { + var valid = ev === 'change' || ev === 'add' + if (opts.hardReload !== false) { + valid = /\.css$/i.test(file) && valid + } + if (valid) { + emitter.reload(file) + } + }) + .on('pending', function () { + if (opts.hardReload !== false) { + emitter.reload(opts.serve) + } + }) + } + if (opts.watch || opts.live) { + var globArray = [].concat(opts.watch).filter(Boolean) + var globs = opts.watch === true ? undefined : globArray + emitter.watch(globs) + } // start portfinding + connect getPorts(opts, handlePorts) return emitter + function reload (file) { + if (!tinylr) return + tinylr.reload(file) + emitter.emit('reload', file) + } + + // enable file watch capabilities + function watch (glob, watchOpt) { + if (!started) { + deferredWatch = emitter.watch.bind(null, glob, watchOpt) + } else { + // destroy previous + if (fileWatcher) fileWatcher.close() + glob = glob && glob.length > 0 ? glob : [ '**/*.{html,css}' ] + fileWatcher = createFileWatch(glob, watchOpt) + fileWatcher.on('watch', emitter.emit.bind(emitter, 'watch')) + } + return emitter + } + + // enables LiveReload capabilities + function live (liveOpts) { + if (!started) { + deferredLive = emitter.live.bind(null, liveOpts) + } else { + // destroy previous + if (tinylr) tinylr.close() + + liveOpts = xtend({ + host: opts.host, + port: opts.livePort + }, liveOpts) + + // inject script tag into HTML requests + server.setLiveOptions(liveOpts) + tinylr = createTinylr(liveOpts) + } + return emitter + } + function handlePorts (err, result) { if (closed) return if (err) { @@ -113,17 +188,22 @@ function createBudo (entries, opts) { function connect () { if (closed) return - running = true + started = true var port = opts.port var uri = 'http://' + hostname + ':' + port + '/' log.info({ message: 'Server running at', url: uri, type: 'connect' }) + // if live() or watch() was called before connection + deferredWatch() + deferredLive() + // provide info on server connection emitter.emit('connect', { uri: uri, port: port, + livePort: opts.livePort, host: hostname, serve: opts.serve, entries: entries, @@ -134,7 +214,10 @@ function createBudo (entries, opts) { function close () { closed = true - if (running) server.close() + if (started) server.close() + if (tinylr) tinylr.close() + if (bundler) bundler.close() + if (fileWatcher) fileWatcher.close() } function bail (msg) { diff --git a/lib/create-bundler.js b/lib/create-bundler.js index 76c6cee..9ebda99 100644 --- a/lib/create-bundler.js +++ b/lib/create-bundler.js @@ -14,12 +14,11 @@ function createBundler (files, opts) { var bundler var args = opts.browserifyArgs - // CLI args for browserify if (args && Array.isArray(args)) { + // CLI args for browserify bundler = fromArgs(args, bOpts) - } - // just assume JS only options - else { + } else { + // just assume JS only options bundler = browserify(bOpts) } @@ -28,7 +27,7 @@ function createBundler (files, opts) { }) return createMiddleware(bundler, { - delay: opts.delay, + delay: opts.delay || 0, errorHandler: true }) } diff --git a/lib/create-server.js b/lib/create-server.js index 8d4a876..d9b13ff 100644 --- a/lib/create-server.js +++ b/lib/create-server.js @@ -24,11 +24,6 @@ function createServer (middleware, opts) { var server = http.createServer(router) var live = opts.live - router.addRoute('/index.html', home) - router.addRoute('/', home) - router.addRoute('*.html', wildcard(true)) - router.addRoute('*', wildcard()) - if (middleware) { router.addRoute('/' + opts.serve, function (req, res) { log.info({ @@ -39,19 +34,25 @@ function createServer (middleware, opts) { }) } + router.addRoute('/index.html', home) + router.addRoute('/', home) + router.addRoute('*.html', wildcard(true)) + router.addRoute('*', wildcard()) + // allow user to toggle live reload integration - server.setLive = setLive + server.setLiveOptions = setLiveOptions return server - function setLive (liveOpts) { + function setLiveOptions (liveOpts) { live = liveOpts } function wildcard (html) { return function (req, res) { // inject LiveReload into HTML content if needed - if (html && live) + if (html && live) { res = inject(res, live) + } log.info({ url: req.url, type: 'static' @@ -85,8 +86,9 @@ function createServer (middleware, opts) { function home (req, res) { fs.exists(path.join(basedir, 'index.html'), function (exists) { // inject LiveReload into HTML content if needed - if (live) + if (live) { res = inject(res, live) + } var type = exists ? 'static' : 'generated' log.info({ diff --git a/lib/file-watch.js b/lib/file-watch.js index 5c15345..5638219 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -17,7 +17,8 @@ module.exports = function (glob, opt) { }, opt) var emitter = new Emitter() - var closed = false, ready = false + var closed = false + var ready = false var watcher = watch(glob, opt) watcher.on('add', onWatch.bind(null, 'add')) diff --git a/lib/map-entry.js b/lib/map-entry.js index 363c929..cbfd3d0 100644 --- a/lib/map-entry.js +++ b/lib/map-entry.js @@ -4,16 +4,21 @@ var url = require('url') module.exports = mapEntry function mapEntry (file) { var parts = file.split(':') - var entry = {} + var pathFrom, pathTo, pathUrl if (parts.length > 1 && parts[1].length > 0) { - entry.from = parts[0] - entry.to = parts[1] + pathFrom = parts[0] + pathTo = parts[1] + pathUrl = pathTo } else { - entry.from = entry.to = file + pathFrom = pathTo = file + pathUrl = path.basename(pathFrom) } - var filepath = path.basename(entry.from) - entry.url = url.parse(filepath).pathname - return entry + pathUrl = url.parse(pathUrl).pathname + return { + url: pathUrl, + from: pathFrom, + to: pathTo + } } diff --git a/lib/parse-args.js b/lib/parse-args.js index 7bd662c..c7b6958 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -13,7 +13,9 @@ function parseArgs (args, opt) { ], string: [ 'host', - 'port' + 'port', + 'dir', + 'serve' ], default: { port: 9966, diff --git a/lib/server.js b/lib/server.js deleted file mode 100644 index 260aa68..0000000 --- a/lib/server.js +++ /dev/null @@ -1,140 +0,0 @@ -var ecstatic = require('ecstatic') -var Router = require('routes-router') -var http = require('http') -var fs = require('fs') -var path = require('path') -var log = require('bole')('budo') -var fs = require('fs') -var inject = require('inject-lr-script') -var Emitter = require('events/') -var url = require('url') -var defaultIndex = require('./default-index') - -module.exports = function(opts) { - var handler = createHandler(opts) - var server = http.createServer(handler.router) - - Object.defineProperty(server, '_live', { - get: function() { - return handler.live - }, - set: function(live) { - handler.live = live - } - }) - - server.update = function(contents) { - handler.pending = false - handler.contents = contents - handler.emit('update') - } - - server.pending = function() { - handler.pending = true - } - - return server -} - -function createHandler(opts) { - var basedir = opts.dir || process.cwd() - var staticHandler = ecstatic(basedir) - var router = Router() - - var defaulLiveOpts = { - host: opts.host, - port: opts.livePort || opts['live-port'] - } - - var emitter = new Emitter() - emitter.live = opts.live ? defaulLiveOpts : null - emitter.router = router - emitter.pending = false - emitter.contents = '' - - router.addRoute('/' + url.parse(opts.serve).pathname, function(req, res) { - log.info({ - url: req.url, - type: 'static' - }) - - if (emitter.pending) { - log.debug("bundle pending") - emitter.once('update', function() { - log.debug("bundle ready") - submit(req, res) - }) - } else { - submit(req, res) - } - }) - - router.addRoute('/index.html', home) - router.addRoute('/', home) - router.addRoute('*.html', wildcard(true)) - router.addRoute('*', wildcard()) - - return emitter - - function pushstate(req, res) { - staticHandler(req, res, function () { - if (opts.pushstate) { - // reset for home - req.url = '/' - res.statusCode = 200 - home(req, res) - } else { - res.end() - } - }) - } - - function wildcard(html) { - return function(req, res) { - //inject LiveReload into HTML content if needed - if (html && emitter.live) - res = inject(res, emitter.live) - log.info({ - url: req.url, - type: 'static' - }) - pushstate(req, res) - } - } - - function submit(req, res) { - res.setHeader('content-type', 'text/javascript') - res.setHeader('content-length', emitter.contents.length); - res.statusCode = req.statusCode || 200 - - res.end(emitter.contents) - } - - function home(req, res) { - fs.exists(path.join(basedir, 'index.html'), function(exists) { - //inject LiveReload into HTML content if needed - if (emitter.live) - res = inject(res, emitter.live) - - var type = exists ? 'static' : 'generated' - log.info({ - url: req.url, - type: type - }) - - if (exists) - staticHandler(req, res) - else - generateIndex(opts.serve, req, res) - }) - } - - function generateIndex(outfile, req, res) { - res.setHeader('content-type', 'text/html') - - var stream = opts.defaultIndex || defaultIndex - stream({ - entry: outfile - }).pipe(res) - } -} \ No newline at end of file diff --git a/lib/tinylr.js b/lib/tinylr.js index 21c268a..11b86f1 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -1,46 +1,44 @@ -//a thin wrapper around tiny-lr module +// a thin wrapper around tiny-lr module var log = require('bole')('budo') var xtend = require('xtend') var tinylr = require('tiny-lr') -module.exports = function(opt) { +module.exports = function (opt) { opt = xtend(opt) opt.host = opt.host || 'localhost' - if (typeof opt.port !== 'number') + if (typeof opt.port !== 'number') { opt.port = 35729 - + } + var server = tinylr() - var closed = false, ready = false + var closed = false + var ready = false - server.listen(opt.port, opt.host, function() { + server.listen(opt.port, opt.host, function () { ready = true - if (closed) - return server.close() - - log.info('livereload running on ' + opt.port) + if (closed) return server.close() + log.info({ message: 'LiveReload running on ' + opt.port }) }) var serverImpl = server.server serverImpl.removeAllListeners('error') - serverImpl.on('error', function(err) { + serverImpl.on('error', function (err) { if (err.code === 'EADDRINUSE') { process.stderr.write('ERROR: livereload not started, port ' + opt.port + ' is in use\n') close() } }) - function close() { - if (closed) - return + function close () { + if (closed) return + if (ready) server.close() closed = true - if (ready) - server.close() } return { close: close, - reload: function reload(path) { + reload: function reload (path) { try { server.changed({ body: { @@ -52,4 +50,4 @@ module.exports = function(opt) { } } } -} \ No newline at end of file +} diff --git a/lib/watchify.js b/lib/watchify.js deleted file mode 100644 index 9d71add..0000000 --- a/lib/watchify.js +++ /dev/null @@ -1,109 +0,0 @@ -var log = require('bole')('budo') -var createWatchify = require('./create-watchify') -var Emitter = require('events/') -var debounce = require('debounce') -var concat = require('concat-stream') - -//Eventually this may split into a watchify-server module -module.exports = function(entries, userArgs, opt) { - var emitter = new Emitter() - var delay = opt.delay - var closed = false - var pending = true - var watchify - var time = Date.now() - - var contents = null - - emitter.close = function() { - if (closed) - return - closed = true - if (watchify) { - //needed for watchify@3.0.0 - //see test-close-immediate - //this needs to be revisited upstream - setTimeout(function() { - watchify.close() - }, 50) - } - } - - createWatchify(entries, userArgs, { - cli: opt.cli, - basedir: opt.dir - }, function(err, instance) { - if (err) { - var msg = [ - err.message, - 'Example:', - ' npm install watchify --save-dev\n' - ].join('\n') - emitter.emit('error', new Error(msg)) - return - } - if (closed) - return - - watchify = instance - var bundleDebounced = debounce(bundle, delay) - watchify.on('update', function() { - emitter.emit('pending') - pending = true - time = Date.now() - bundleDebounced() - }) - - //initial bundle - time = Date.now() - emitter.emit('pending') - pending = true - bundle() - }) - - function bundle() { - if (closed) { - update() - return - } - - var didError = false - - var outStream = concat(function(body) { - if (!didError) { - contents = body - bundleEnd() - } - }) - - var wb = watchify.bundle() - wb.on('error', function(err) { - err = String(err) - console.error(err) - contents = 'console.error(' + JSON.stringify(err) + ');' - didError = true - bundleEnd() - }) - wb.pipe(outStream) - - function bundleEnd() { - if (opt.verbose && !didError) { - var delay = Date.now() - time - log.info({ - elapsed: Math.round(delay) + 'ms', - type: 'bundle', - url: opt.serve - }) - } - update() - } - } - return emitter - - function update() { - if (pending) { - pending = false - emitter.emit('update', contents) - } - } -} \ No newline at end of file diff --git a/package.json b/package.json index 7cb2514..49f7d52 100644 --- a/package.json +++ b/package.json @@ -60,13 +60,7 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "silent": "./bin/cmd.js example/app.js:bundle.js --dir example --stream false", - "start": "./bin/cmd.js example/app.js:bundle.js --dir example --verbose | garnish", - "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -v | garnish -v", - "live-plugin": "./bin/cmd.js example/app.js:bundle.js --dir example --live-plugin -v | garnish", - "brfs1": "./bin/cmd.js example/app.js:bundle.js --dir example -v -t [ brfs --foo ] | garnish", - "brfs2": "./bin/cmd.js example/app.js:bundle.js --dir example -v -- -t [ brfs --foo ] | garnish", - "remap": "./bin/cmd.js example/*.js --serve bundle2.js --dir example --live -v | garnish" + "simple": "budo --dir example --live example/simple.js:bundle.js" }, "keywords": [ "browserify", From 2b18803aa5aad398ae609b5e7c76678c8757cba1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 6 Sep 2015 11:17:07 +1000 Subject: [PATCH 139/302] docs: fix typo in example --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be8f5d9..9534747 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ By default, messages will be printed to `process.stdout`, and `--debug` will be Everything after `--` is passed directly to browserify; this is currently needed for subarg syntax. Example: ```js -budo index.js --live -- -t [ babelify --exetensions .es6 ] +budo index.js --live -- -t [ babelify --extensions .es6 ] ``` ### API @@ -112,4 +112,4 @@ The original motivation for making budō was to build an *experimental* tool and ## License -MIT, see [LICENSE.md](http://github.com/mattdesl/budo/blob/master/LICENSE.md) for details. \ No newline at end of file +MIT, see [LICENSE.md](http://github.com/mattdesl/budo/blob/master/LICENSE.md) for details. From 335454b7762bf9a873ac20a0b98585a33760780a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 6 Sep 2015 11:45:00 -0400 Subject: [PATCH 140/302] more cleanup --- index.js | 66 +---- lib/budo.js | 353 ++++++++++++++------------ lib/{create-bundler.js => bundler.js} | 0 lib/create-budo.js | 229 ----------------- lib/create-watchify.js | 95 ------- lib/get-server-path.js | 30 --- lib/index.js | 98 ------- lib/parse-args.js | 1 + lib/{create-server.js => server.js} | 27 +- package.json | 4 +- 10 files changed, 229 insertions(+), 674 deletions(-) rename lib/{create-bundler.js => bundler.js} (100%) delete mode 100644 lib/create-budo.js delete mode 100644 lib/create-watchify.js delete mode 100644 lib/get-server-path.js delete mode 100644 lib/index.js rename lib/{create-server.js => server.js} (82%) diff --git a/index.js b/index.js index 60c4c37..583cd5f 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ var parseArgs = require('./lib/parse-args') -var budo = require('./lib/create-budo') +var budo = require('./lib/budo') var color = require('term-color') module.exports = budo @@ -37,6 +37,11 @@ function budoCLI (args, opts) { delete argv.outfile } + // opts.live can be a match glob or a boolean + if (/(true|false)/.test(argv.live)) { + argv.live = argv.live === 'true' + } + return budo(entries, argv).on('error', exit) } @@ -44,62 +49,3 @@ function exit (err) { console.log(color.red('ERROR'), err.message) process.exit(1) } - -/* -module.exports.cli = function cli(args) { - var getport = require('getport') - var opts = require('minimist')(args, { - boolean: ['stream'], - default: { stream: true }, - '--': true - }) - - if (isSubargError(args)) { - console.error("ERROR: You must use -- for browserify's subarg syntax") - console.error("Example:\n budo index.js -- -t [ babelify --extensions .babel ]") - process.exit(1) - } - - //user can silent budo with --no-stream - if (opts.stream !== false) { - opts.stream = process.stdout - } - - var entries = opts._ - delete opts._ - - var showHelp = opts.h || opts.help - - if (showHelp) { - var vers = require('./package.json').version - console.log('budo ' + vers, '\n') - var help = require('path').join(__dirname, 'bin', 'help.txt') - require('fs').createReadStream(help) - .pipe(process.stdout) - return - } - - if (!entries || entries.filter(Boolean).length === 0) { - console.error('ERROR:\n no entry scripts specified\n use --help for examples') - process.exit(1) - } - - var basePort = opts.port || 9966 - getport(basePort, function(err, port) { - if (err) { - console.error("Could not find available port", err) - process.exit(1) - } - opts.port = port - create(entries, opts, true) - .on('error', function(err) { - //Some more helpful error messaging - if (err.message === 'listen EADDRINUSE') - console.error("Port", port, "is not available\n") - else - console.error('Error:\n ' + err.message) - process.exit(1) - }) - }) -} -*/ diff --git a/lib/budo.js b/lib/budo.js index 22a493d..05ce84a 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -1,192 +1,231 @@ -var Emitter = require('events/') +var bole = require('bole') +var log = bole('budo') var xtend = require('xtend') -var assign = require('xtend/mutable') -var log = require('bole')('budo') +var once = require('once') +var path = require('path') +var EventEmitter = require('events').EventEmitter +var isMatch = require('micromatch').isMatch +var noop = function () {} +var getPorts = require('./get-ports') var createServer = require('./server') +var createBundler = require('./bundler') var createFileWatch = require('./file-watch') var createTinylr = require('./tinylr') -var createWatchify = require('./watchify') +var mapEntry = require('./map-entry') + +module.exports = createBudo +function createBudo (entries, opts) { + // if no entries are specified + if (entries && !Array.isArray(entries) && typeof entries === 'object') { + opts = entries + entries = [] + } -var DEFAULT_DELAY = 0 + // do not mutate user options + opts = xtend({ portfind: true }, opts) -module.exports = function() { - var emitter = new Emitter() - var started = false + // perhaps later this will be configurable + opts.cwd = process.cwd() + + // log to output stream + if (opts.stream) { + bole.output({ + stream: opts.stream, + level: 'debug' + }) + } + + // optionally allow as arrays + entries = [].concat(entries).filter(Boolean) + + var entryObjects = entries.map(mapEntry) + var entryFiles = entryObjects.map(function (entry) { + return entry.from + }) + + if (opts.serve && typeof opts.serve !== 'string') { + throw new TypeError('opts.serve must be a string or undefined') + } else if (!opts.serve && entries.length > 0) { + opts.serve = entryObjects[0].url + } + + // default to cwd + if (!opts.dir || opts.dir.length === 0) { + opts.dir = opts.cwd + } + + var hostname = (opts.host || 'localhost') + var emitter = new EventEmitter() + var bundler + + if (entries.length === 0) { + return bail('no entry files specified!') + } + + bundler = createBundler(entryFiles, opts) + bundler.on('log', function (ev) { + if (ev.type === 'bundle') { + ev.url = '/' + opts.serve + ev.elapsed = ev.elapsed + 'ms' + } + log.info(ev) + }) + + bundler.on('update', emitter.emit.bind(emitter, 'update')) + bundler.on('pending', emitter.emit.bind(emitter, 'pending')) + + var server = createServer(bundler.middleware, opts) var closed = false - var serveAs - - var watchify - var server - var watcher - var tinylr - var defaultGlobs - var defaultLiveOpts - var defaultWatchOpts - var deferreds = [] - - emitter._start = function(entries, opt, cli) { - opt = opt || {} - var port = opt.port - server = createServer(opt) - .on('error', function(err) { - emitter.emit('error', err) - }) - .listen(port, opt.host, function() { - var hostname = (opt.host || 'localhost') - var uri = "http://" + hostname + ":" + opt.port + "/" - - log.info({ message: "Server running at " + uri, type: 'connect' }) - - //defaults for watch() function - defaultGlobs = ['**/*.{html,css}'] - - //watchify@3 has some extra options - var ignoreWatchArg = typeof opt['ignore-watch'] === 'undefined' - ? opt.iw - : opt['ignore-watch'] - defaultWatchOpts = { - poll: opt.poll, - 'ignore-watch': opt.ignoreWatch || ignoreWatchArg - } - //and default live options - defaultLiveOpts = { - plugin: opt.livePlugin || opt['live-plugin'], - host: opt.host, - port: opt.livePort || opt['live-port'] + var started = false + var fileWatcher = null + var tinylr = null + var deferredWatch = noop + var deferredLive = noop + + // public API + emitter.close = once(close) + emitter.reload = reload + emitter.live = live + emitter.watch = watch + + // setup defaults for live reload / watchify + if (opts.live) { + emitter + .watch() + .live() + .on('watch', function (ev, file) { + if (ev !== 'change' && ev !== 'add') { + return } - - //start watchify process - runWatchify(entries, opt, cli) - - serveAs = opt.serve - started = true - - //if user wanted live() or watch() enabled - deferreds.forEach(function(func) { - func() - }) - deferreds.length = 0 - - //finally, emit callback with some info - emitter.emit('connect', { - uri: uri, - port: opt.port, - host: hostname, - serve: opt.serve, - entries: entries, - dir: opt.dir - }) + defaultFileEvent(file) + }) + .on('pending', function () { + defaultFileEvent(opts.serve) }) - return emitter } - - //no-op until live() is enabled - emitter.reload = noop - //enable file watch capabilities - emitter.watch = function(glob, watchOpt) { - if (!started) { - deferreds.push(emitter.watch.bind(null, glob, watchOpt)) + // start portfinding + connect + getPorts(opts, handlePorts) + + return emitter + + function defaultFileEvent (file) { + var filename = path.basename(file) + if (typeof opts.live === 'string' && !isMatch(filename, opts.live)) { + return } - else { - watchOpt = xtend(defaultWatchOpts, watchOpt) - watchOpt = getChokidarOpts(watchOpt) //transform to chokidar - watcher = createFileWatch(glob || defaultGlobs, watchOpt) - watcher.on('watch', emitter.emit.bind(emitter, 'watch')) - emitter.watch = noop + emitter.reload(file) + } + + function reload (file) { + if (!tinylr) return + tinylr.reload(file) + emitter.emit('reload', file) + } + + // enable file watch capabilities + function watch (glob, watchOpt) { + if (!started) { + deferredWatch = emitter.watch.bind(null, glob, watchOpt) + } else { + // destroy previous + if (fileWatcher) fileWatcher.close() + glob = glob && glob.length > 0 ? glob : [ '**/*.{html,css}' ] + watchOpt = xtend({ poll: opts.poll }, watchOpt) + + fileWatcher = createFileWatch(glob, watchOpt) + fileWatcher.on('watch', emitter.emit.bind(emitter, 'watch')) } return emitter } - //enable live-reload capabilities - emitter.live = function(liveOpts) { + // enables LiveReload capabilities + function live (liveOpts) { if (!started) { - deferreds.push(emitter.live.bind(null, liveOpts)) + deferredLive = emitter.live.bind(null, liveOpts) } else { - liveOpts = xtend(defaultLiveOpts, liveOpts) - - //if plugin, ignore live reload in HTML, - //otherwise inject with new options (host/port) - server._live = liveOpts.plugin ? null : liveOpts - + // destroy previous + if (tinylr) tinylr.close() + + liveOpts = xtend({ + host: opts.host, + port: opts.livePort + }, liveOpts) + + // inject script tag into HTML requests + server.setLiveOptions(liveOpts) tinylr = createTinylr(liveOpts) - emitter.reload = function(file) { - tinylr.reload(file) - emitter.emit('reload', file) - } - emitter.live = noop } return emitter } - //close everything - emitter.close = function() { - if (closed) - return emitter - closed = true - if (watchify) - watchify.close() - if (watcher) - watcher.close() - if (tinylr) - tinylr.close() - if (server) - server.close() - emitter.live = noop - emitter.watch = noop - emitter.emit('exit') - return emitter - } + function handlePorts (err, result) { + if (closed) return + if (err) { + emitter.emit('error', err) + return + } - return emitter + opts.port = result.port - function runWatchify(entries, opt, cli) { - if (closed) - return + // only override if we actually needed to find livePort + if (typeof result.livePort !== 'undefined') { + opts.livePort = result.livePort + } - //create a new watchify instance - watchify = createWatchify(entries, opt, { - cli: cli, //whether to use watchify/bin/args or not - dir: opt.dir, - serve: opt.serve, - verbose: opt.v || opt.verbose, - delay: typeof opt.delay === 'number' ? opt.delay : DEFAULT_DELAY - }) - .on('update', function(contents) { - if (server) - server.update(contents) - emitter.emit('update', serveAs, contents) - }) - .on('pending', function() { - if (server) - server.pending() - emitter.emit('pending', serveAs) + // improve error messaging + server.on('error', function (err) { + if (err.code === 'EADDRINUSE') { + err.message = 'port ' + opts.port + ' is in use' + emitter.emit('error', err) + } else { + emitter.emit('error', err) + } }) - .on('error', emitter.emit.bind(emitter, 'error')) - } - function noop() { - return emitter + // start server + server.listen(opts.port, opts.host, connect) } -} -function getChokidarOpts(opt) { - //do not mutate original opts - opt = assign({}, opt) + function connect () { + if (closed) return + started = true + + var port = opts.port + var uri = 'http://' + hostname + ':' + port + '/' + + log.info({ message: 'Server running at', url: uri, type: 'connect' }) + + // if live() or watch() was called before connection + deferredWatch() + deferredLive() + + // provide info on server connection + emitter.emit('connect', { + uri: uri, + port: port, + livePort: opts.livePort, + host: hostname, + serve: opts.serve, + entries: entries, + static: opts.static, + dir: opts.dir + }) + } - if (opt.poll || opt.poll === 0) { - var interval = opt.poll - opt.usePolling = true - opt.interval = typeof interval === 'number' ? interval : 100 - delete opt.poll + function close () { + closed = true + if (started) server.close() + if (tinylr) tinylr.close() + if (bundler) bundler.close() + if (fileWatcher) fileWatcher.close() } - if (opt['ignore-watch'] || typeof opt['ignore-watch'] === 'string') { - opt.ignored = opt['ignore-watch'] - delete opt['ignore-watch'] - } else { - //otherwise let file-watch ignore some defaults - delete opt.ignored + + function bail (msg) { + process.nextTick(function () { + emitter.emit('error', new Error(msg)) + }) + return emitter } - return opt } diff --git a/lib/create-bundler.js b/lib/bundler.js similarity index 100% rename from lib/create-bundler.js rename to lib/bundler.js diff --git a/lib/create-budo.js b/lib/create-budo.js deleted file mode 100644 index 34a1e1c..0000000 --- a/lib/create-budo.js +++ /dev/null @@ -1,229 +0,0 @@ -var bole = require('bole') -var log = bole('budo') -var xtend = require('xtend') -var once = require('once') -var path = require('path') -var EventEmitter = require('events').EventEmitter -var noop = function () {} - -var getPorts = require('./get-ports') -var createServer = require('./create-server') -var createBundler = require('./create-bundler') -var createFileWatch = require('./file-watch') -var createTinylr = require('./tinylr') - -var mapEntry = require('./map-entry') - -module.exports = createBudo -function createBudo (entries, opts) { - // if no entries are specified - if (entries && !Array.isArray(entries) && typeof entries === 'object') { - opts = entries - entries = [] - } - - // do not mutate user options - opts = xtend({ portfind: true }, opts) - - // perhaps later this will be configurable - opts.cwd = process.cwd() - - // log to output stream - if (opts.stream) { - bole.output({ - stream: opts.stream, - level: 'debug' - }) - } - - // optionally allow as arrays - entries = [].concat(entries).filter(Boolean) - - var entryObjects = entries.map(mapEntry) - var entryFiles = entryObjects.map(function (entry) { - return entry.from - }) - - if (opts.serve && typeof opts.serve !== 'string') { - throw new TypeError('opts.serve must be a string or undefined') - } else if (!opts.serve && entries.length > 0) { - opts.serve = entryObjects[0].url - } - - // default to cwd - if (!opts.dir || opts.dir.length === 0) { - opts.dir = opts.cwd - } - - var hostname = (opts.host || 'localhost') - var emitter = new EventEmitter() - var bundler - - if (entries.length === 0) { - return bail('no entry files specified!') - } - - bundler = createBundler(entryFiles, opts) - bundler.on('log', function (ev) { - if (ev.type === 'bundle') { - ev.url = '/' + opts.serve - ev.elapsed = ev.elapsed + 'ms' - } - log.info(ev) - }) - - bundler.on('update', emitter.emit.bind(emitter, 'update')) - bundler.on('pending', emitter.emit.bind(emitter, 'pending')) - - var server = createServer(bundler.middleware, opts) - var closed = false - var started = false - var fileWatcher = null - var tinylr = null - var deferredWatch = noop - var deferredLive = noop - - // public API - emitter.close = once(close) - emitter.reload = reload - emitter.live = live - emitter.watch = watch - - // setup defaults for live reload / watchify - if (opts.live) { - emitter.live() - .on('watch', function (ev, file) { - var valid = ev === 'change' || ev === 'add' - if (opts.hardReload !== false) { - valid = /\.css$/i.test(file) && valid - } - if (valid) { - emitter.reload(file) - } - }) - .on('pending', function () { - if (opts.hardReload !== false) { - emitter.reload(opts.serve) - } - }) - } - if (opts.watch || opts.live) { - var globArray = [].concat(opts.watch).filter(Boolean) - var globs = opts.watch === true ? undefined : globArray - emitter.watch(globs) - } - - // start portfinding + connect - getPorts(opts, handlePorts) - - return emitter - - function reload (file) { - if (!tinylr) return - tinylr.reload(file) - emitter.emit('reload', file) - } - - // enable file watch capabilities - function watch (glob, watchOpt) { - if (!started) { - deferredWatch = emitter.watch.bind(null, glob, watchOpt) - } else { - // destroy previous - if (fileWatcher) fileWatcher.close() - glob = glob && glob.length > 0 ? glob : [ '**/*.{html,css}' ] - fileWatcher = createFileWatch(glob, watchOpt) - fileWatcher.on('watch', emitter.emit.bind(emitter, 'watch')) - } - return emitter - } - - // enables LiveReload capabilities - function live (liveOpts) { - if (!started) { - deferredLive = emitter.live.bind(null, liveOpts) - } else { - // destroy previous - if (tinylr) tinylr.close() - - liveOpts = xtend({ - host: opts.host, - port: opts.livePort - }, liveOpts) - - // inject script tag into HTML requests - server.setLiveOptions(liveOpts) - tinylr = createTinylr(liveOpts) - } - return emitter - } - - function handlePorts (err, result) { - if (closed) return - if (err) { - emitter.emit('error', err) - return - } - - opts.port = result.port - - // only override if we actually needed to find livePort - if (typeof result.livePort !== 'undefined') { - opts.livePort = result.livePort - } - - // improve error messaging - server.on('error', function (err) { - if (err.code === 'EADDRINUSE') { - err.message = 'port ' + opts.port + ' is in use' - emitter.emit('error', err) - } else { - emitter.emit('error', err) - } - }) - - // start server - server.listen(opts.port, opts.host, connect) - } - - function connect () { - if (closed) return - started = true - - var port = opts.port - var uri = 'http://' + hostname + ':' + port + '/' - - log.info({ message: 'Server running at', url: uri, type: 'connect' }) - - // if live() or watch() was called before connection - deferredWatch() - deferredLive() - - // provide info on server connection - emitter.emit('connect', { - uri: uri, - port: port, - livePort: opts.livePort, - host: hostname, - serve: opts.serve, - entries: entries, - static: opts.static, - dir: opts.dir - }) - } - - function close () { - closed = true - if (started) server.close() - if (tinylr) tinylr.close() - if (bundler) bundler.close() - if (fileWatcher) fileWatcher.close() - } - - function bail (msg) { - process.nextTick(function () { - emitter.emit('error', new Error(msg)) - }) - return emitter - } -} diff --git a/lib/create-watchify.js b/lib/create-watchify.js deleted file mode 100644 index a8dcf20..0000000 --- a/lib/create-watchify.js +++ /dev/null @@ -1,95 +0,0 @@ -var path = require('path') -var assign = require('xtend/mutable') -var dargs = require('dargs') -var resolve = require('resolve') -var watchify = require('watchify') -var fromArgs = require('watchify/bin/args') - -//finds watchify in local or global -//also handles whether to use bin/args or not (for CLI) -module.exports = function (entries, userArgs, opt, cb) { - var useCLI = opt.cli - if (useCLI) { - //use fromArgs for the CLI arguments - var cliArgs = getCLIArgs(entries, userArgs) - var instance = fromArgs(cliArgs) - process.nextTick(function() { - cb(null, instance) - }) - } else { - //determine where watchify is stored locally (relative to budo) - resolve('watchify', { basedir: __dirname }, function(err, index) { - if (err) return cb(err) - //now determine where that watchify's browserify is stored (relative to the local watchify) - var watchifyPath = path.dirname(index) - resolve('browserify', { basedir: watchifyPath }, function(err, index) { - if (err) return cb(err) - cb(null, fromAPI(index, entries, userArgs)) - }) - }) - } -} - -function fromAPI(browserifyIndex, entries, userArgs) { - var browserify = require(browserifyIndex) - - userArgs = getDefaultArgs(userArgs) - var b = browserify(userArgs) - var instance = watchify(b, userArgs) - entries.forEach(function(entry) { - instance.add(path.resolve(entry)) - }) - return instance -} - -function getDefaultArgs(opt) { - //ensure we have watchify args setup - opt = assign({ cache: {}, packageCache: {} }, opt) - - //disable watchify delay since we will handle debouncing manually - opt.delay = 0 - - //handle debug flags - var debug = typeof opt.debug === 'undefined' ? opt.d : opt.debug - - //if minimist didn't parse it as a boolean (since it can be a string too) - if (debug === 'false') - debug = false - if (debug === 'true') - debug = true - - //enable debug by default - if (debug !== false) { - delete opt.d - //allow string like 'eval' but otherwise specify as true - opt.debug = debug || true - } - //if user explicitly disabled debug, we can't have it passed - //at all to watchify/browserify - else if (debug === false) { - delete opt.d - delete opt.debug - } - - //clean up some possible collisions with budo - removeCollisions(opt) - return opt -} - -function getCLIArgs(entries, opt) { - var unparsedArgs = opt['--'] || [] - opt = getDefaultArgs(opt) - return entries.concat(dargs(opt)).concat(unparsedArgs) -} - -function removeCollisions(opt) { - var collisions = [ - 'dir', 'o', 'outfile', 'port', - 'host', 'live', 'serve', 'live-port', - 'live-plugin', 'defaultIndex', 'livePort', - 'livePlugin', 'stream', '--' - ] - collisions.forEach(function(col) { - delete opt[col] - }) -} \ No newline at end of file diff --git a/lib/get-server-path.js b/lib/get-server-path.js deleted file mode 100644 index d486668..0000000 --- a/lib/get-server-path.js +++ /dev/null @@ -1,30 +0,0 @@ -var url = require('url') -var path = require('path') - -var defaultName = 'bundle.js' -var fixable = /^.[\\\/]+/ - -// This could be improved to create better -// consistency with relative/absolute/etc paths -// PRs/ideas welcome :) -// https://github.com/mattdesl/budo/issues/42 - -module.exports = function cleanPath (file, serveAs) { - // no entry, or "./" or "." - // just use "bundle.js" in this case - if (!file || /^\.[\/\\]*$/.test(file)) { - return defaultName - } - - // if "./" or ".\\" is starting - // we can just fix it by chopping that off - if (fixable.test(file)) { - file = file.replace(fixable, '') - } else if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/.test(file)) { - // otherwise, see if path is relative/absolute - return path.basename(file) - } - - // path is not relative/absolute, just treat as usual - return url.parse(file).path -} diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 8333593..0000000 --- a/lib/index.js +++ /dev/null @@ -1,98 +0,0 @@ -var bole = require('bole') -var log = bole('budo') -var xtend = require('xtend') -var budo = require('./budo') -var url = require('url') -var getServerPath = require('./get-server-path') - -module.exports = function createBudo(entry, opts, cli) { - var argv = xtend(opts) - - if (argv.stream) { - bole.output({ - stream: argv.stream, - level: 'debug' - }) - } - - var emitter = budo() - - if (argv.o || argv.outfile) { - console.error('Warning: --outfile has been removed in budo@3.0') - //ensure we don't pass to watchify - delete argv.o - delete argv.outfile - } - - var entries = Array.isArray(entry) ? entry : [entry] - entries = entries.filter(Boolean) - if (entries.length === 0) { - return bail("No entry scripts specified!") - } - - //clean up entries and take the first one for bundle mapping - var file - entries = entries.map(function(entry, i) { - var map = mapping(entry) - if (i === 0) - file = map.to - return map.from - }) - - //if user specified --serve use that as our entry map - var serveAs = argv.serve - argv.port = typeof argv.port === 'number' ? argv.port : 9966 - argv.dir = argv.dir || process.cwd() - - //user requested a --serve path, use as-is - if (serveAs && typeof serveAs === 'string') - file = serveAs - //otherwise try to glean from file path (relative, absolute) - else - file = getServerPath(file) - argv.serve = file - - if (typeof argv.dir !== 'string') - return bail('--dir must be a path') - - //run watchify server - emitter.on('connect', setupLive) - emitter._start(entries, argv, cli) - .on('exit', function() { - log.info('closing') - }) - - return emitter - - //if user requested live: true, set it up with some defaults - function setupLive() { - if (argv.live || argv.livePlugin || argv['live-plugin']) { - emitter - .watch() - .live() - .on('watch', function(ev, file) { - //HTML/CSS changes - if (ev === 'change' || ev === 'add') - emitter.reload(file) - }) - .on('pending', function(file) { - emitter.reload(file) - }) - } - } - - function mapping(entry) { - var parts = entry.split(':') - if (parts.length > 1 && parts[1].length > 0) { - return { from: parts[0], to: parts[1] } - } - return { from: entry, to: entry } - } - - function bail(msg) { - process.nextTick(function() { - emitter.emit('error', new Error(msg)) - }) - return emitter - } -} \ No newline at end of file diff --git a/lib/parse-args.js b/lib/parse-args.js index c7b6958..4705c0a 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -12,6 +12,7 @@ function parseArgs (args, opt) { 'portfind' ], string: [ + 'live', 'host', 'port', 'dir', diff --git a/lib/create-server.js b/lib/server.js similarity index 82% rename from lib/create-server.js rename to lib/server.js index d9b13ff..d2e0885 100644 --- a/lib/create-server.js +++ b/lib/server.js @@ -8,7 +8,7 @@ var fs = require('fs') var log = require('bole')('budo') module.exports = createServer -function createServer (middleware, opts) { +function createServer (entryHandler, opts) { var router = Router() var staticPaths = [].concat(opts.dir).filter(Boolean) if (staticPaths.length === 0) { @@ -21,16 +21,31 @@ function createServer (middleware, opts) { return ecstatic({ root: filepath, handleError: last }) }) - var server = http.createServer(router) var live = opts.live + var middleware = opts.middleware || defaultMiddleware + if (typeof middleware !== 'function') { + throw new TypeError('expected opts.middleware to be a function') + } - if (middleware) { + var server = http.createServer(function (req, res) { + if (middleware.length === 3) { + // user is handling next() + middleware(req, res, function () { + router(req, res) + }) + } else { + middleware(req, res) + router(req, res) + } + }) + + if (entryHandler) { router.addRoute('/' + opts.serve, function (req, res) { log.info({ url: req.url, type: 'generated' }) - middleware(req, res) + entryHandler(req, res) }) } @@ -43,6 +58,10 @@ function createServer (middleware, opts) { server.setLiveOptions = setLiveOptions return server + function defaultMiddleware (req, res, next) { + next() + } + function setLiveOptions (liveOpts) { live = liveOpts } diff --git a/package.json b/package.json index 49f7d52..784ccdf 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", + "micromatch": "^2.2.0", + "minimatch": "^2.0.10", "minimist": "^1.1.0", "once": "^1.3.2", "path-is-absolute": "^1.0.0", @@ -60,7 +62,7 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", - "simple": "budo --dir example --live example/simple.js:bundle.js" + "simple": "budo --dir example --live=*.css example/simple.js:bundle.js" }, "keywords": [ "browserify", From 8d732ed56f5cec85e07268e050ead51f0819b9f2 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 6 Sep 2015 12:46:27 -0400 Subject: [PATCH 141/302] adding more changes and a change log --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++++ index.js | 5 ++-- lib/budo.js | 13 +++++++++- lib/bundler.js | 8 +++++- lib/error-handler.js | 41 +++++++++++++++++++++++++++++++ lib/parse-args.js | 6 ++++- package.json | 4 ++- test/test-next.js | 5 +++- 8 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 lib/error-handler.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..182c277 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,58 @@ +# 5.0.0 + +##### Major Changes + +- the `' } -function getHTML() { +function getHTML () { return '' -} \ No newline at end of file +} diff --git a/test/test-next.js b/test/test-portfind.js similarity index 75% rename from test/test-next.js rename to test/test-portfind.js index 2a00964..6c39eab 100644 --- a/test/test-next.js +++ b/test/test-portfind.js @@ -3,10 +3,12 @@ var budo = require('../') var http = require('http') var path = require('path') +var file = path.join(__dirname, 'fixtures', 'app.js') + test('user can disable portfinding', function (t) { t.plan(1) var server = http.createServer().listen(9966, function () { - var b = budo({ + var b = budo(file, { port: 9966, portfind: false }) @@ -19,16 +21,16 @@ test('user can disable portfinding', function (t) { }) test('portfinds by default', function (t) { - t.plan(1) + t.plan(2) var server = http.createServer().listen(9966, function () { - var b = budo({ + var b = budo(file, { port: 9966, - // livePort: 9966 + livePort: 30000 }) b.on('error', t.fail) b.on('connect', function (ev) { - t.equal(ev.port, 9967) - // t.equal(ev.livePort, 9968) + t.equal(ev.port, 9967, 'gets port') + t.equal(ev.livePort, 30000, 'gets live port') b.close() server.close() }) @@ -37,11 +39,12 @@ test('portfinds by default', function (t) { test('gets connect', function (t) { t.plan(1) - var b = budo({ + var b = budo(file, { port: 9966, portfind: false }) b.on('connect', function (ev) { t.equal(ev.port, 9966) + b.close() }) }) diff --git a/test/test-server.js b/test/test-server.js index 7cdbff2..1426737 100644 --- a/test/test-server.js +++ b/test/test-server.js @@ -7,35 +7,35 @@ var entry = 'test/fixtures/app.js' test('default should serve on 9966', port(9966)) test('should serve on specified port', port(3000, { port: 3000 })) -test('should serve on --dir', function(t) { +test('should serve on --dir', function (t) { t.plan(2) var app = budo(entry, { dir: __dirname }) - .on('connect', function(ev) { + .on('connect', function (ev) { request.get({ uri: ev.uri + 'fixtures/text.txt' - }, function(err, resp, body) { + }, function (err, resp, body) { if (err) t.fail(err) t.equal(body.toString(), 'foobar', 'text matches') app.close() }) }) - .on('exit', function() { + .on('exit', function () { t.ok(true, 'closed') }) .on('error', t.fail.bind(t)) }) -function port(expected, opt) { - return function(t) { +function port (expected, opt) { + return function (t) { t.plan(2) var app = budo(entry, opt) - .on('connect', function(ev) { - t.ok(ev.port, expected, 'serves on '+expected) + .on('connect', function (ev) { + t.ok(ev.port, expected, 'serves on ' + expected) app.close() }) - .on('exit', function() { + .on('exit', function () { t.ok(true, 'closed') }) .on('error', t.fail.bind(t)) } -} \ No newline at end of file +} From 25e0c55562fc30ce1a0566742e39fbfb9816e0ba Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 12:12:30 -0400 Subject: [PATCH 147/302] fixing tests and live reload order --- lib/budo.js | 14 ++--- test/test-api.js | 142 ++++++++++++++++++++++++---------------------- test/test-live.js | 2 +- 3 files changed, 80 insertions(+), 78 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 4233db1..fc064c5 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -99,14 +99,6 @@ function createBudo (entries, opts) { // setup defaults for live reload / watchify if (opts.live) { - emitter.once('connect', defaultLive) - } - - // start portfinding + connect - getPorts(opts, handlePorts) - return emitter - - function defaultLive () { emitter .watch() .live() @@ -121,6 +113,10 @@ function createBudo (entries, opts) { }) } + // start portfinding + connect + getPorts(opts, handlePorts) + return emitter + function defaultFileEvent (file) { var filename = path.basename(file) if (typeof opts.live === 'string' && !isMatch(filename, opts.live)) { @@ -130,7 +126,7 @@ function createBudo (entries, opts) { } function reload (file) { - emitter.emit('reload', file) + process.nextTick(emitter.emit.bind(emitter, 'reload', file)) if (tinylr) { tinylr.reload(file) } diff --git a/test/test-api.js b/test/test-api.js index 535bf76..c9c008e 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -2,65 +2,65 @@ var test = require('tape') var budo = require('../') var through = require('through2') -test('gets connect info', function (t) { - t.plan(7) - t.timeoutAfter(10000) +// test('gets connect info', function (t) { +// t.plan(7) +// t.timeoutAfter(10000) - var app = budo('test/fixtures/app.js', { - dir: __dirname, - port: 8000 - }) - .on('error', function (err) { - t.fail(err) - }) - .on('connect', function (ev) { - t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') - t.equal(ev.serve, 'app.js', 'mapping matches') - t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') - t.equal(ev.host, 'localhost', 'host is not specified') - t.equal(ev.port, 8000, 'port matches') - t.equal(ev.dir, __dirname, 'dir matches') - app.close() - }) - .on('reload', function () { - t.fail('should not have received reload event') - }) - .on('watch', function () { - t.fail('should not have received watch event') - }) - .on('exit', function () { - t.ok(true, 'closing') - }) -}) +// var app = budo('test/fixtures/app.js', { +// dir: __dirname, +// port: 8000 +// }) +// .on('error', function (err) { +// t.fail(err) +// }) +// .on('connect', function (ev) { +// t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') +// t.equal(ev.serve, 'app.js', 'mapping matches') +// t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') +// t.equal(ev.host, 'localhost', 'host is not specified') +// t.equal(ev.port, 8000, 'port matches') +// t.equal(ev.dir, __dirname, 'dir matches') +// app.close() +// }) +// .on('reload', function () { +// t.fail('should not have received reload event') +// }) +// .on('watch', function () { +// t.fail('should not have received watch event') +// }) +// .on('exit', function () { +// t.ok(true, 'closing') +// }) +// }) -test('entry mapping', function (t) { - t.plan(2) - t.timeoutAfter(10000) +// test('entry mapping', function (t) { +// t.plan(2) +// t.timeoutAfter(10000) - var app = budo(['test/fixtures/app:foo.js', 'test/fixtures/with space.js']) - .on('connect', function (ev) { - t.equal(ev.serve, 'foo.js', 'mapping matches') - t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') - app.close() - }) -}) +// var app = budo(['test/fixtures/app:foo.js', 'test/fixtures/with space.js']) +// .on('connect', function (ev) { +// t.equal(ev.serve, 'foo.js', 'mapping matches') +// t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') +// app.close() +// }) +// }) -test('--serve allows explicit bundle renaming', function (t) { - t.plan(2) - t.timeoutAfter(2000) +// test('--serve allows explicit bundle renaming', function (t) { +// t.plan(2) +// t.timeoutAfter(2000) - var app = budo(['test/fixtures/app', 'test/fixtures/with space.js'], { - serve: 'static/foo.js' - }) - .on('connect', function (ev) { - t.equal(ev.serve, 'static/foo.js', 'mapping matches') - t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') - app.close() - }) -}) +// var app = budo(['test/fixtures/app', 'test/fixtures/with space.js'], { +// serve: 'static/foo.js' +// }) +// .on('connect', function (ev) { +// t.equal(ev.serve, 'static/foo.js', 'mapping matches') +// t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') +// app.close() +// }) +// }) test('sets watch() and live() by default with live: true', function (t) { - t.plan(3) + t.plan(4) t.timeoutAfter(3000) var app = budo('test/fixtures/app.js', { @@ -68,11 +68,26 @@ test('sets watch() and live() by default with live: true', function (t) { port: 8000, live: true }) + // the order is pending -> reload -> update + .once('pending', function () { + // bundle.js started changing + t.ok(true, 'got pending') + }) .once('update', function () { // bundle.js changed t.ok(true, 'got update event') + app.close() + }) + .once('reload', function () { + // LiveReload triggered + t.ok(true, 'got reload event') + }) + .on('error', function (err) { + t.fail(err) + }) + .on('exit', function () { + t.ok(true, 'closing') }) - testLive(t, app) }) test('should pipe JSON to specified stream', function (t) { @@ -103,25 +118,16 @@ test('allow setting live() manually', function (t) { port: 8000, live: false }) - .live() // start live server + .live() // start live server with watchify .on('update', function () { - t.ok(true, 'got first update') + t.ok(true, 'got update') app.reload() }) - testLive(t, app) -}) - -function testLive (t, app) { - app - .once('reload', function () { - // LiveReload triggered - t.ok(true, 'got reload event') + .on('reload', function () { + t.ok(true, 'got reload') app.close() }) - .on('error', function (err) { - t.fail(err) - }) .on('exit', function () { - t.ok(true, 'closing') + t.ok(true, 'got exit') }) -} +}) diff --git a/test/test-live.js b/test/test-live.js index 29e75a2..2fcd3a1 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -21,10 +21,10 @@ test('should inject LiveReload snippet', function (t) { }) .once('update', function () { t.ok(true, 'update event triggered') + app.close() }) .on('reload', function (file) { t.equal(file, 'app.js', 'reload event triggered') - app.close() }) .on('connect', function (ev) { matchesHTML(t, ev.uri) From 622e2516a1cde8ab49e0bcf20442e2d7a94c9dfd Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 12:24:11 -0400 Subject: [PATCH 148/302] add a default route for favicon to avoid annoying 404 error --- index.js | 1 + lib/budo.js | 49 +++++++++++++++++++++++------------------ lib/server.js | 8 +++++++ package.json | 13 ----------- test/test-no-entries.js | 0 5 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 test/test-no-entries.js diff --git a/index.js b/index.js index aca6af4..df99ca9 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ module.exports.cli = budoCLI function budoCLI (args, opts) { var argv = parseArgs(args, opts) + // default to stdout if (argv.stream !== false) { argv.stream = process.stdout } diff --git a/lib/budo.js b/lib/budo.js index fc064c5..9c967b9 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -18,7 +18,7 @@ var log = bole('budo') module.exports = createBudo function createBudo (entries, opts) { - // if no entries are specified + // if no entries are specified, just options if (entries && !Array.isArray(entries) && typeof entries === 'object') { opts = entries entries = [] @@ -59,31 +59,36 @@ function createBudo (entries, opts) { var hostname = (opts.host || 'localhost') var emitter = new EventEmitter() - var bundler + var bundler, middleware - if (entries.length === 0) { - return bail('no entry files specified!') - } + // if (entries.length === 0) { + // return bail('no entry files specified!') + // } - bundler = createBundler(entryFiles, opts) - bundler.on('log', function (ev) { - if (ev.type === 'bundle') { - ev.url = '/' + opts.serve - ev.name = 'browserify' - ev.elapsed = ev.elapsed + 'ms' - } - log.info(ev) - }) + if (entries.length > 0) { + bundler = createBundler(entryFiles, opts) + middleware = bundler.middleware + + bundler.on('log', function (ev) { + if (ev.type === 'bundle') { + ev.url = '/' + opts.serve + ev.name = 'browserify' + ev.elapsed = ev.elapsed + 'ms' + } + log.info(ev) + }) + + // uncaught syntax errors should not stop the server + // this only happens when errorHandler: false + bundler.on('error', function (err) { + console.error('Error:', err.message ? err.message : err) + }) + bundler.on('update', emitter.emit.bind(emitter, 'update')) + bundler.on('pending', emitter.emit.bind(emitter, 'pending')) + } - // uncaught syntax errors should not stop the server - // this only happens when errorHandler: false - bundler.on('error', function (err) { - console.error('Error:', err.message ? err.message : err) - }) - bundler.on('update', emitter.emit.bind(emitter, 'update')) - bundler.on('pending', emitter.emit.bind(emitter, 'pending')) - var server = createServer(bundler.middleware, opts) + var server = createServer(middleware, opts) var closed = false var started = false var fileWatcher = null diff --git a/lib/server.js b/lib/server.js index 71d2e2f..f2deec9 100644 --- a/lib/server.js +++ b/lib/server.js @@ -50,6 +50,7 @@ function createServer (entryHandler, opts) { router.addRoute('/index.html', home) router.addRoute('/', home) + router.addRoute('/favicon.ico', favicon) router.addRoute('*.html', wildcard(true)) router.addRoute('*', wildcard()) @@ -69,6 +70,13 @@ function createServer (entryHandler, opts) { }) } + function favicon (req, res) { + res.setHeader('Content-Type', 'image/x-icon') + res.statusCode = 200 + logger('generated', req, res) + res.end() + } + function defaultMiddleware (req, res, next) { next() } diff --git a/package.json b/package.json index 03e5b89..221fa15 100644 --- a/package.json +++ b/package.json @@ -13,36 +13,23 @@ "url": "https://github.com/mattdesl" }, "dependencies": { - "array-find": "^1.0.0", - "async": "^1.4.2", "async-each": "^0.1.6", - "async-filter-each": "^1.0.0", "bole": "^2.0.0", "browserify": "^11.0.1", "chokidar": "^1.0.1", - "concat-stream": "^1.4.8", - "dargs": "^4.0.0", - "debounce": "^1.0.0", "ecstatic": "^0.7.2", "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", "micromatch": "^2.2.0", - "minimatch": "^2.0.10", "minimist": "^1.1.0", "once": "^1.3.2", "opn": "^3.0.2", - "opnr": "^1.0.4", - "path-is-absolute": "^1.0.0", - "patterns": "^1.0.2", - "resolve": "^1.1.6", "routes-router": "^4.1.2", "simple-html-index": "^1.1.0", "strip-ansi": "^3.0.0", "term-color": "^1.0.1", - "through2": "^0.6.3", "tiny-lr": "^0.1.5", - "watchify": "^3.3.1", "watchify-middleware": "^1.0.0", "xtend": "^4.0.0" }, diff --git a/test/test-no-entries.js b/test/test-no-entries.js new file mode 100644 index 0000000..e69de29 From 92a10f4de69bbb8e0e89505e753b9ac222e993b8 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 13:03:46 -0400 Subject: [PATCH 149/302] add logs to middleware requests --- lib/budo.js | 13 +---- lib/log-http-request.js | 1 + lib/server.js | 25 +++++++--- package.json | 1 + test/test-api.js | 102 ++++++++++++++++++++-------------------- test/test-no-entries.js | 72 ++++++++++++++++++++++++++++ test/test-server.js | 18 +++++++ 7 files changed, 162 insertions(+), 70 deletions(-) create mode 100644 lib/log-http-request.js diff --git a/lib/budo.js b/lib/budo.js index 9c967b9..a7e373d 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -26,6 +26,7 @@ function createBudo (entries, opts) { // do not mutate user options opts = xtend({ portfind: true }, opts) + entries = entries || [] // perhaps later this will be configurable opts.cwd = process.cwd() @@ -61,10 +62,6 @@ function createBudo (entries, opts) { var emitter = new EventEmitter() var bundler, middleware - // if (entries.length === 0) { - // return bail('no entry files specified!') - // } - if (entries.length > 0) { bundler = createBundler(entryFiles, opts) middleware = bundler.middleware @@ -87,7 +84,6 @@ function createBudo (entries, opts) { bundler.on('pending', emitter.emit.bind(emitter, 'pending')) } - var server = createServer(middleware, opts) var closed = false var started = false @@ -242,11 +238,4 @@ function createBudo (entries, opts) { closed = true started = false } - - function bail (msg) { - process.nextTick(function () { - emitter.emit('error', new Error(msg)) - }) - return emitter - } } diff --git a/lib/log-http-request.js b/lib/log-http-request.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/log-http-request.js @@ -0,0 +1 @@ + diff --git a/lib/server.js b/lib/server.js index f2deec9..ac0a2bf 100644 --- a/lib/server.js +++ b/lib/server.js @@ -22,6 +22,7 @@ function createServer (entryHandler, opts) { return ecstatic({ root: filepath, handleError: last }) }) + var entrySrc = opts.serve var live = opts.live var middleware = opts.middleware || defaultMiddleware if (typeof middleware !== 'function') { @@ -30,18 +31,21 @@ function createServer (entryHandler, opts) { var server = http.createServer(function (req, res) { if (middleware.length === 3) { - // user is handling next() + var removeLogger = logger('middleware', req, res) + // user wants to specify which routes to fall through to budo middleware(req, res, function () { + removeLogger() router(req, res) }) } else { + // all routes will fall through to budo routes middleware(req, res) router(req, res) } }) if (entryHandler) { - var entryRoute = urlParse(opts.serve).pathname + var entryRoute = urlParse(entrySrc).pathname router.addRoute('/' + entryRoute, function (req, res) { logger('generated', req, res) entryHandler(req, res) @@ -56,10 +60,11 @@ function createServer (entryHandler, opts) { // allow user to toggle live reload integration server.setLiveOptions = setLiveOptions + return server function logger (type, req, res) { - res.once('finish', function () { + var fn = function () { log.info({ name: 'http', message: (req.method || 'GET').toUpperCase(), @@ -67,7 +72,13 @@ function createServer (entryHandler, opts) { statusCode: res.statusCode, type: type }) - }) + } + res.once('finish', fn) + + // allow removal + return function () { + res.removeListener('finish', fn) + } } function favicon (req, res) { @@ -131,17 +142,17 @@ function createServer (entryHandler, opts) { if (exists) { staticRequest(req, res) } else { - generateIndex(opts.serve, req, res) + generateIndex(req, res) } }) } - function generateIndex (outfile, req, res) { + function generateIndex (req, res) { res.setHeader('content-type', 'text/html') var stream = opts.defaultIndex || defaultIndex stream({ - entry: outfile + entry: entrySrc }).pipe(res) } } diff --git a/package.json b/package.json index 221fa15..565012f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "domready": "^1.0.7", "garnish": "^2.1.2", "inject-lr-script": "^1.0.1", + "ndjson": "^1.4.1", "raf-loop": "^1.0.1", "request": "^2.53.0", "tap-spec": "^3.0.0", diff --git a/test/test-api.js b/test/test-api.js index c9c008e..f98905f 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -2,62 +2,62 @@ var test = require('tape') var budo = require('../') var through = require('through2') -// test('gets connect info', function (t) { -// t.plan(7) -// t.timeoutAfter(10000) +test('gets connect info', function (t) { + t.plan(7) + t.timeoutAfter(10000) -// var app = budo('test/fixtures/app.js', { -// dir: __dirname, -// port: 8000 -// }) -// .on('error', function (err) { -// t.fail(err) -// }) -// .on('connect', function (ev) { -// t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') -// t.equal(ev.serve, 'app.js', 'mapping matches') -// t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') -// t.equal(ev.host, 'localhost', 'host is not specified') -// t.equal(ev.port, 8000, 'port matches') -// t.equal(ev.dir, __dirname, 'dir matches') -// app.close() -// }) -// .on('reload', function () { -// t.fail('should not have received reload event') -// }) -// .on('watch', function () { -// t.fail('should not have received watch event') -// }) -// .on('exit', function () { -// t.ok(true, 'closing') -// }) -// }) + var app = budo('test/fixtures/app.js', { + dir: __dirname, + port: 8000 + }) + .on('error', function (err) { + t.fail(err) + }) + .on('connect', function (ev) { + t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') + t.equal(ev.serve, 'app.js', 'mapping matches') + t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') + t.equal(ev.host, 'localhost', 'host is not specified') + t.equal(ev.port, 8000, 'port matches') + t.equal(ev.dir, __dirname, 'dir matches') + app.close() + }) + .on('reload', function () { + t.fail('should not have received reload event') + }) + .on('watch', function () { + t.fail('should not have received watch event') + }) + .on('exit', function () { + t.ok(true, 'closing') + }) +}) -// test('entry mapping', function (t) { -// t.plan(2) -// t.timeoutAfter(10000) +test('entry mapping', function (t) { + t.plan(2) + t.timeoutAfter(10000) -// var app = budo(['test/fixtures/app:foo.js', 'test/fixtures/with space.js']) -// .on('connect', function (ev) { -// t.equal(ev.serve, 'foo.js', 'mapping matches') -// t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') -// app.close() -// }) -// }) + var app = budo(['test/fixtures/app:foo.js', 'test/fixtures/with space.js']) + .on('connect', function (ev) { + t.equal(ev.serve, 'foo.js', 'mapping matches') + t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') + app.close() + }) +}) -// test('--serve allows explicit bundle renaming', function (t) { -// t.plan(2) -// t.timeoutAfter(2000) +test('--serve allows explicit bundle renaming', function (t) { + t.plan(2) + t.timeoutAfter(2000) -// var app = budo(['test/fixtures/app', 'test/fixtures/with space.js'], { -// serve: 'static/foo.js' -// }) -// .on('connect', function (ev) { -// t.equal(ev.serve, 'static/foo.js', 'mapping matches') -// t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') -// app.close() -// }) -// }) + var app = budo(['test/fixtures/app', 'test/fixtures/with space.js'], { + serve: 'static/foo.js' + }) + .on('connect', function (ev) { + t.equal(ev.serve, 'static/foo.js', 'mapping matches') + t.deepEqual(ev.entries, ['test/fixtures/app', 'test/fixtures/with space.js'], 'from matches') + app.close() + }) +}) test('sets watch() and live() by default with live: true', function (t) { t.plan(4) diff --git a/test/test-no-entries.js b/test/test-no-entries.js index e69de29..01a9d76 100644 --- a/test/test-no-entries.js +++ b/test/test-no-entries.js @@ -0,0 +1,72 @@ +var budo = require('../') +var test = require('tape') +var ndjson = require('ndjson') +var request = require('request') + +// an HTML page with no ' + +test('no arguments needed', function (t) { + t.plan(4) + var b = budo() + .on('connect', function (ev) { + t.deepEqual(ev.entries, []) + t.deepEqual(ev.serve, undefined) + request.get({ url: ev.uri }, function (err, resp, body) { + if (err) return t.fail(err) + t.equal(resp.statusCode, 200, 'still gets generated index.html') + t.equal(body, defaultIndex) + b.close() + }) + }) +}) + +test('still supports serve', function (t) { + t.plan(4) + var b = budo({ + serve: 'foo.js' + }) + .on('connect', function (ev) { + t.deepEqual(ev.entries, []) + t.deepEqual(ev.serve, 'foo.js') + request.get({ url: ev.uri }, function (err, resp, body) { + if (err) return t.fail(err) + t.equal(resp.statusCode, 200, 'still gets generated index.html') + t.equal(body, scriptIndex) + b.close() + }) + }) +}) + +test('user can build their own server', function (t) { + t.plan(5) + var stream = ndjson.parse() + stream.on('data', function (data) { + if (data.name === 'http' && data.url === '/foo.js') { + t.equal(data.type, 'middleware') + } + }) + + var b = budo({ + serve: 'foo.js', + stream: stream, + middleware: function (req, res, next) { + if (req.url === '/foo.js') { + res.end('hello world') + } else { + next() + } + } + }) + .on('connect', function (ev) { + t.deepEqual(ev.entries, []) + t.deepEqual(ev.serve, 'foo.js') + request.get({ url: ev.uri + 'foo.js' }, function (err, resp, body) { + if (err) return t.fail(err) + t.equal(resp.statusCode, 200, 'gets foo.js route') + t.equal(body, 'hello world') + b.close() + }) + }) +}) diff --git a/test/test-server.js b/test/test-server.js index 1426737..c5fa490 100644 --- a/test/test-server.js +++ b/test/test-server.js @@ -25,6 +25,24 @@ test('should serve on --dir', function (t) { .on('error', t.fail.bind(t)) }) +test('favicon.ico should have status code 200', function (t) { + t.plan(2) + var app = budo(entry, { dir: __dirname }) + .on('connect', function (ev) { + request.get({ + uri: ev.uri + 'favicon.ico' + }, function (err, resp) { + if (err) t.fail(err) + t.equal(resp.statusCode, 200) + app.close() + }) + }) + .on('exit', function () { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) +}) + function port (expected, opt) { return function (t) { t.plan(2) From 07e1a667112f47e76705acccb558ca67d567c86f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 14:29:25 -0400 Subject: [PATCH 150/302] updating docs, examples --- CHANGELOG.md | 1 + README.md | 67 +++++---- bin/help.txt | 6 +- docs/api.md | 235 ++++++++++++++++++++++++++++++++ docs/basics.md | 44 +++--- docs/comparisons.md | 29 ---- docs/programmatic-usage.md | 123 ----------------- example/app.js | 18 +-- example/budo-less.js | 42 ++++++ example/index.html | 2 +- example/{theme.css => main.css} | 0 example/style.less | 3 + lib/file-watch.js | 1 + lib/log-http-request.js | 1 - lib/server.js | 6 +- package.json | 5 +- 16 files changed, 357 insertions(+), 226 deletions(-) create mode 100644 docs/api.md delete mode 100644 docs/comparisons.md delete mode 100644 docs/programmatic-usage.md create mode 100644 example/budo-less.js rename example/{theme.css => main.css} (100%) create mode 100644 example/style.less delete mode 100644 lib/log-http-request.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 073e78f..4bf5e11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - portfinding is enabled by default in API and CLI - user can disbale with `--no-portfind` or `portfind: false` - removed `--verbose`, `-v`, timing is logged by default now +- entry files are now optional (i.e. if you just need a static HTML with LiveReload) - added `--open`, `-o` to launch browser on connect - syntax errors in code are shown in the DOM body now - can disable with `--no-error-handler` diff --git a/README.md b/README.md index be8f5d9..14ee23e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) -This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but specifically focused on incremental reloading and LiveReload integration (including CSS injection). +This is a browserify development server inspired by [beefy](https://github.com/chrisdickinson/beefy) and [wzrd](https://github.com/maxogden/wzrd), but specifically focused on incremental reloading, LiveReload integration (including CSS injection), and other high-level features. To install: @@ -16,17 +16,16 @@ Running budo will start a server with a default `index.html` and incrementally b # serve file on port 9966 budo index.js -# enable LiveReload on html/css/js changes -# show timing information on re-bundle -budo index.js --verbose --live +# enable LiveReload on HTML/CSS/JS file changes +budo index.js --live # pass some options to browserify -budo index.js --live -- -t babelify --full-paths +budo index.js --live -- -t babelify ``` -Then open [http://localhost:9966](http://localhost:9966) to see the content in action. +Then open [http://localhost:9966/](http://localhost:9966/) to see the content in action. -To pretty-print in terminal, [garnish](https://github.com/mattdesl/garnish), [bistre](https://github.com/hughsk/bistre) or another [ndjson](http://ndjson.org)-based stream can be used. Example: +Budo emits [ndjson](http://ndjson.org), so a pretty-printer like [garnish](https://github.com/mattdesl/garnish) or [bistre](https://github.com/hughsk/bistre) is recommended for better logging. Example: ```sh # install garnish if you don't have it @@ -36,17 +35,20 @@ npm install garnish -g budo index.js | garnish ``` +Result: + +
+ See [docs](#docs) for more features. PRs/suggestions/comments welcome. ## docs - [basic usage](docs/basics.md) -- [comparisons](docs/comparisons.md) - [API and integrations (Gulp, Grunt, npm scripts)](docs/programmatic-usage.md) - [error reporting](docs/errors.md) - [running tests and examples](docs/tests-and-examples.md) -- [script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) - [rapid prototyping with budō](http://mattdesl.svbtle.com/rapid-prototyping) +- [experimental script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) ## usage @@ -54,61 +56,58 @@ See [docs](#docs) for more features. PRs/suggestions/comments welcome. ### CLI -Details for `budo` command-line interface. Other options (like `-t`) will be sent to browserify. +Details for `budo` command-line interface. ```sh Usage: - budo [entries] [opts] + budo index.js [opts] -- [browserify opts] Options: --help, -h show help message - --port the port to run, default 9966 - --host the host, default "localhost" - --dir the directory to serve, and the base for --outfile - --serve override the bundle path being served - --live enable LiveReload integration - --live-plugin enable LiveReload but do not inject script tag - --live-port the LiveReload port, default 35729 - --pushstate always render the index page instead of a 404 page - --verbose, -v verbose timing information for re-bundles + --version show version + --port, -p the port to run, default 9966 + --host, -H the host, default "localhost" + --dir, -d a path, or array of paths for base static content + --serve, -s override the bundle path being served + --live, -l enable default LiveReload integration + --live-port, -L the LiveReload port, default 35729 + --open, -o launch the browser once connected + --pushstate, -P always render the index page instead of a 404 page --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout --no-debug do not use inline source maps + --no-portfind will not attempt auto-portfinding + --no-error-handler disable default DOM error handling ``` By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. -Everything after `--` is passed directly to browserify; this is currently needed for subarg syntax. Example: +Everything after `--` is passed directly to browserify. Example: ```js -budo index.js --live -- -t [ babelify --exetensions .es6 ] +budo index.js --live -- -t [ babelify --exetension .es6 ] ``` ### API -The API mirrors the CLI except it does not write to `process.stdout` by default, and does not attempt to find available ports from a base port. +The API mirrors the CLI except it does not write to `process.stdout` by default. ```js var budo = require('budo') budo('./src/index.js', { - live: true, //live reload - stream: process.stdout, //log to stdout - port: 8000 //use this port + live: true, // default live reload + stream: process.stdout, // log to stdout + port: 8000, // use this port + portfind: false // emit error if port is in use }).on('connnect', function(ev) { //... }) ``` -See [API usage](docs/programmatic-usage.md) for more details. - -## Script Injection +There is also a `budo.cli()` entry point, if you wish to imitate the CLI more accurately. -[![screenshot](http://i.imgur.com/LJP7d9I.png)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) - -[(click for demo)](https://www.youtube.com/watch?v=cfgeN3G_Gl0) - -The original motivation for making budō was to build an *experimental* tool and proof-of-concept around Chrome Script Injection. This has since split off into its own repository: [budo-chrome](https://github.com/mattdesl/budo-chrome). +See [API usage](docs/programmatic-usage.md) for more details. ## License diff --git a/bin/help.txt b/bin/help.txt index f7028a2..2fab944 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -8,14 +8,16 @@ Options: --host, -H the host, default "localhost" --dir, -d a path, or array of paths for base static content --serve, -s override the bundle path being served - --live, -l enable LiveReload integration + --live, -l enable default LiveReload integration --live-port, -L the LiveReload port, default 35729 + --open, -o launch the browser once connected --pushstate, -P always render the index page instead of a 404 page --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding - + --no-error-handler disable default DOM error handling + Browserify Options: https://github.com/substack/node-browserify diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..52a1b0f --- /dev/null +++ b/docs/api.md @@ -0,0 +1,235 @@ +# API + +The API mirrors the CLI except you must provide a `stream` for logging. + +#### `b = budo(entry[, opts])` + +Sets up a new instance of `budo`, where `entry` is a path or or array of paths. + +The options are the same as CLI, except that the API does not print to stdout. You can specify a `stream` option to print to. + +The return value is an event emitter. + +Example: + +```js +var budo = require('budo') +budo('./src/index.js', { + live: true, // live reload + stream: process.stdout, // log to stdout + port: 8000 // use this as the base port +}).on('connnect', function(ev) { + //... +}) +``` + +##### `opts` + +All options available through the API: + +- `port` (Number) + - the base port to use for the development server (default `9966`) +- `livePort` (Number) + - the base port to use for the LiveReload server (default `35729`) +- `portfind` (Boolean) + - whether to use portfinding to find the next available ports (default `true`) +- `host` (String) the host to listen on (default `'localhost'`) +- `live` (Boolean|String) + - whether to set up a default LiveReload integration + - if a string is specified, only filenames matching that glob + will trigger LiveReload events +- `open` (Boolean) + - whether to launch the browser (default `false`) +- `dir` (String|Array) + - a folder or list of folders to use as the base path for static assets (default `process.cwd()`) + - a default `index.html` will be searched in the first `dir` folder +- `stream` (writable stream) + - a writable stream like `process.stdout` for ndjson logging (default `undefined`) +- `debug` (Boolean) + - whether to enable source maps from browserify (default `true`) +- `serve` (String) + - if specified, the ` @@ -54,7 +56,7 @@ For local tools, we need to use [npm-scripts](https://docs.npmjs.com/misc/script ```sh "scripts": { - "start": "budo index.js --verbose | garnish" + "start": "budo index.js | garnish" }, ``` @@ -74,7 +76,11 @@ budo index.js --live | garnish Now when you save the `index.js` file, it will trigger a LiveReload event on your `localhost:9966` tab after watchify has finished bundling. It also listens to HTML and CSS reload, and injects stylesheets without a page refresh. -Alternatively, you can use `--live-plugin` if you want to enable LiveReload through the browser extension (e.g. [for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)). In this case, no script is injected into the HTML, and you need to [enable LiveReload manually](https://github.com/mattdesl/wtch#setup). +From the command line, you can specify a filename glob to only trigger LiveReload in those cases. For example, to only allow CSS and HTML changes to trigger a LiveReload: + +```sh +budo index.js --live=*.{html,css} +``` ## multiple entries @@ -86,39 +92,25 @@ budo test/*.js --serve static/bundle.js | garnish *Note:* This uses unix glob expansion and may not work on Windows. -## unparsed arguments +## browserify arguments Everything after the `--` argument will not be parsed/manipulated, and will be passed directly to browserify. Currently, this is needed when using sub-arg syntax: ```sh -budo main.js --live -v -- -t [ foo --bar=555 --debug ] +budo main.js --live -- -t [ foo --bar=555 --debug ] ``` -## browser launcher +## launch -You can use [opnr](https://github.com/mattdesl/opnr) to launch the browser when an available port is found. Install it like so: +To launch the browser once the server connects, you can use the `--open` or `-o` flag: ```sh -npm install opnr -g +budo index.js --open | garnish ``` -Now, pipe `opnr` before your pretty-printing, and the browser will open when ready. - -```sh -budo index.js | opnr | garnish -``` - -With `npm scripts` it might look like this: - -```json -"scripts": { - "dev": "budo index.js", - "start": "npm run dev | garnish", - "open": "npm run dev | opnr | garnish" -} -``` +Also see [opnr](https://github.com/mattdesl/opnr), which allows for a similar functionality without forcing it as a command-line flag. ## internal IP @@ -133,3 +125,5 @@ npm install internal-ip -g # run using internal IP budo index.js --host=`internal-ip` | opnr | garnish ``` + +*Note:* This uses unix features and may not work in Windows. \ No newline at end of file diff --git a/docs/comparisons.md b/docs/comparisons.md deleted file mode 100644 index 3780d0b..0000000 --- a/docs/comparisons.md +++ /dev/null @@ -1,29 +0,0 @@ -# comparisons - -## budō - -[budō](https://github.com/mattdesl/budo) lies somewhere between the rich feature set of [beefy](#beefy) and the small focus of [wzrd](#wzrd). It has a stronger focus on LiveReload and incremental bundling, suspending the server response until the bundle is complete (i.e. will not serve empty/stale bundles). It is also the base for more experimental features like [Chrome script injection](https://github.com/mattdesl/budo-chrome) and [React hot module replacement](https://gist.github.com/mattdesl/2aa5b45ed1f230635a04). - -## beefy - -[beefy](https://github.com/chrisdickinson/beefy) is a feature-rich dev tool for browserify, and much of the inspiration for this project. It has a wide scope, encompassing browserify and watchify, and takes a different approach to live reload. - -```sh -#example ... -beefy index.js --open -``` - -## wzrd - -[wzrd](https://github.com/maxogden/wzrd) is a tiny spin-off of beefy that is ideal for [local dependencies](https://github.com/stackgl/learning-webgl-03/blob/db8f36a534b2a184924f8b890014ff3dd9a5b391/package.json#L6-L9). It has a small and focused scope, and encourages composition and diversity with other tools (e.g. [garnish](https://github.com/mattdesl/garnish) for pretty-printing). - -Features like live reload and incremental bundling are likely outside of its scope. - -```sh -#example ... -wzrd index.js:bundle.js | garnish -``` - -## garnish - -[garnish](https://github.com/mattdesl/garnish) simply prettifies bole and ndjson log output from tools that decide to use it. This includes wzrd, wtch, and budō. \ No newline at end of file diff --git a/docs/programmatic-usage.md b/docs/programmatic-usage.md deleted file mode 100644 index 0721cb9..0000000 --- a/docs/programmatic-usage.md +++ /dev/null @@ -1,123 +0,0 @@ -# API - -The API mirrors the CLI except you must provide a `stream` for logging, and it does not attempt to auto-portfind. You can also pass in transforms and plugins as an object, like so: - -```js -var budo = require('budo') -var babelify = require('babelify') - -budo('./src/index.js', { - live: true, //live reload - transform: babelify, //ES6 transpiling - stream: process.stdout, //log to stdout - port: 8000 //use this port -}).on('connnect', function(ev) { - //... -}) -``` - -#### `b = budo(entry[, opts])` - -Spins up a new instance of `budo`, where `entry` is a path or or array of paths. - -The options are the same as CLI, except that the API does not attempt auto portfinding, and does not print to stdout. You can specify a `stream` option to print to. - -The return value is an event emitter. - -#### `b.on('exit')` - -Called when the server is closed. - -#### `b.on('error')` - -Called on a fatal error, like not being able to create the server. - -#### `b.on('connect')` - -Called once the budo server connects. The callback is passed an `event` object that looks like this: - -```js -{ - uri: 'http://localhost:9966/', //served URI - serve: 'bundle/entry%20file.js' //the URL path for our entry file - dir: 'app', //the working directory being served - host: 'localhost', //defaults to localhost - port: 9966 //the port we're running on -} -``` - -#### `b.on('update')` - -This event is triggered when the bundle changes. It is passed the following: - -```(urlPath, src)``` - -Where `urlPath` is the served name (e.g. `bundle%20file.js`) and `src` is the new contents of the bundle. - -#### `b.on('reload')` - -If live reload is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after the LiveReload has been sent. The parameter is `file`, the file path being submitted to the LiveReload server. - -#### `b.on('watch')` - -If file watching is enabeld (i.e. through `live` or `live-plugin`), this event will be triggered after HTML and CSS files have changed. The parameters will be `(eventType, file)` where `eventType` could be "add", "change", "unlink", etc. - -#### `b.reload(path)` - -If live reload is enabled (i.e. through `live` or `live-plugin`), this will send a LiveReload event to the given path and then trigger the `"reload"` event. - -#### `b.live([opt])` - -If `live` and `live-plugin` were not specified, you can manually enable the LiveReload server with the specified options object: `port` (default 35729) and `host` (default to the `host` argument provided to budo, or `localhost`). You can also specify `plugin: true` if you do not want the LiveReload snippet injected into the HTML. - -#### `b.watch([globs, chokidarOpts])` - -If `live` and `live-plugin` were not specified, you can manually enabe [chokidar's](https://github.com/paulmillr/chokidar) file watching with the specified `globs` (array or string) and options. - -`globs` defaults to watching `**/*.{html,css}`. `chokidarOpts` defaults to the options passed to the budo constructor. - -Example of using `live()` and `watch()` together. This will only trigger LiveReload on CSS updates. - -```js -var budo = require('budo') -var path = require('path') -var app = budo('index.js') - -app - //listen to CSS changes - .watch('*.css', { interval: 300, usePolling: true }) - //start LiveReload server - .live() - //handle file events - .on('watch', function(type, file) { - //tell LiveReload to inject some CSS - if (path.extname(file) === '.css') - app.reload(file) - }) -``` - -# build tools - -Budo doesn't need a Grunt or Gulp specific plugin to work, but you may choose to wrap it within your favourite task runner for consistency. A simple case might look like this: - -```js -var gulp = require('gulp') -var budo = require('budo') - -//start our local development server -gulp.task('dev', function(cb) { - budo('index.js') - .on('connect', function(ev) { - console.log("Server started at "+ev.uri) - }) - .on('exit', cb) -}) -``` - -Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and serve the browserified contents of `index.js`. - -#### integrations - -- [gulp](https://github.com/mattdesl/budo-gulp-starter) -- [npm scripts](https://gist.github.com/mattdesl/b6990e7c7221c9cc05aa) -- [LiveReactLoad](https://gist.github.com/mattdesl/2aa5b45ed1f230635a04) \ No newline at end of file diff --git a/example/app.js b/example/app.js index 54320d8..80459a7 100644 --- a/example/app.js +++ b/example/app.js @@ -1,30 +1,30 @@ -var dpr = window.devicePixelRatio||1 +var dpr = window.devicePixelRatio || 1 var ctx = require('2d-context')() var fit = require('canvas-fit')(ctx.canvas, window, dpr) -//setup canvas DOM state +// setup canvas DOM state window.addEventListener('resize', fit, false) -require('domready')(function() { +require('domready')(function () { fit() document.body.appendChild(ctx.canvas) -}) +}) var img = new Image() img.src = 'baboon.png' var time = 0 -require('raf-loop')(function(dt) { +require('raf-loop')(function (dt) { var width = ctx.canvas.width, height = ctx.canvas.height ctx.clearRect(0, 0, width, height) - time += dt/1000 + time += dt / 1000 ctx.save() ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time)*50 + 300, 50, 20, 40) - ctx.fillText("from browserify!", 40, 40) + ctx.fillRect(Math.sin(time) * 50 + 300, 50, 20, 40) + ctx.fillText('from browserify!', 40, 40) if (img.width > 0 || img.height > 0) ctx.drawImage(img, 50, 50) ctx.restore() -}).start() \ No newline at end of file +}).start() diff --git a/example/budo-less.js b/example/budo-less.js new file mode 100644 index 0000000..f7268b4 --- /dev/null +++ b/example/budo-less.js @@ -0,0 +1,42 @@ +var budo = require('../') +var less = require('less-css-stream') +var fs = require('fs') +var path = require('path') +var url = require('url') + +// tell script to run from this +process.chdir(__dirname) + +var lessEntry = path.resolve(__dirname, 'style.less') +var cssEntry = 'main.css' +var app = budo('./app.js', { + serve: 'bundle.js', + dir: __dirname, + stream: process.stdout, + middleware: middleware +}).live() + .watch(['**/*.{html,less}']) + .on('watch', function (ev, file) { + if (/\.less$/i.test(file)) { + // tell app to re-request CSS entry point + app.reload(cssEntry) + } else { + app.reload(file) + } + }) + .on('pending', function () { + // also trigger JS hard reloads + app.reload() + }) + +// compile style.less to main.css +function middleware (req, res, next) { + if (url.parse(req.url).pathname === '/' + cssEntry) { + res.setHeader('Content-Type', 'text/css') + fs.createReadStream(lessEntry) + .pipe(less(lessEntry)) + .pipe(res) + } else { + next() + } +} diff --git a/example/index.html b/example/index.html index 17ef952..9ddaf55 100644 --- a/example/index.html +++ b/example/index.html @@ -2,7 +2,7 @@ - + budo diff --git a/example/theme.css b/example/main.css similarity index 100% rename from example/theme.css rename to example/main.css diff --git a/example/style.less b/example/style.less new file mode 100644 index 0000000..c489d30 --- /dev/null +++ b/example/style.less @@ -0,0 +1,3 @@ +body { + background: red; +} \ No newline at end of file diff --git a/lib/file-watch.js b/lib/file-watch.js index f0c8365..0c02322 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -12,6 +12,7 @@ var ignores = [ module.exports = function (glob, opt) { opt = xtend({ + usePolling: opt && opt.poll, ignored: ignores, ignoreInitial: true }, opt) diff --git a/lib/log-http-request.js b/lib/log-http-request.js deleted file mode 100644 index 8b13789..0000000 --- a/lib/log-http-request.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/server.js b/lib/server.js index ac0a2bf..fee5692 100644 --- a/lib/server.js +++ b/lib/server.js @@ -39,6 +39,7 @@ function createServer (entryHandler, opts) { }) } else { // all routes will fall through to budo routes + // we won't add a logger here to avoid doubling up middleware(req, res) router(req, res) } @@ -82,9 +83,12 @@ function createServer (entryHandler, opts) { } function favicon (req, res) { + var maxAge = 345600 // 4 days + res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(maxAge / 1000)) res.setHeader('Content-Type', 'image/x-icon') res.statusCode = 200 - logger('generated', req, res) + // should we clutter logs with favicon.ico requests? + // logger('generated', req, res) res.end() } diff --git a/package.json b/package.json index 565012f..9e69ab7 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,10 @@ "browserify": "^9.0.8", "canvas-fit": "^1.2.0", "domready": "^1.0.7", - "garnish": "^2.1.2", + "garnish": "^3.0.0", "inject-lr-script": "^1.0.1", + "less-css-stream": "^1.0.0", + "less-stream": "^0.1.4", "ndjson": "^1.4.1", "raf-loop": "^1.0.1", "request": "^2.53.0", @@ -53,6 +55,7 @@ }, "scripts": { "test": "tape test/test*.js | tap-spec", + "budo-less": "node example/budo-less | garnish", "simple": "budo --dir example --live example/simple.js:bundle.js -- -t babelify" }, "keywords": [ From 514217386383904e158d099b3c418eaa79e40d62 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 14:29:35 -0400 Subject: [PATCH 151/302] updating deps --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 9e69ab7..a8c8545 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "garnish": "^3.0.0", "inject-lr-script": "^1.0.1", "less-css-stream": "^1.0.0", - "less-stream": "^0.1.4", "ndjson": "^1.4.1", "raf-loop": "^1.0.1", "request": "^2.53.0", From 66abd5813d7eafc83acc1786552fcf67ec286a8e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 18:04:15 -0400 Subject: [PATCH 152/302] fixing code style, using standard, fixing dependencies in package json --- README.md | 4 +--- bin/cmd.js | 8 ++++---- example/app.js | 8 +++++--- example/other.js | 2 +- package.json | 6 ++++-- test/fixtures/app.js | 2 +- test/fixtures/first.js | 2 +- test/fixtures/second.js | 2 +- test/fixtures/with space.js | 2 +- test/test-bundle.js | 11 ++++++----- 10 files changed, 25 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 14ee23e..9f28996 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,7 @@ budo('./src/index.js', { }) ``` -There is also a `budo.cli()` entry point, if you wish to imitate the CLI more accurately. - -See [API usage](docs/programmatic-usage.md) for more details. +See [API usage](docs/api-usage.md) for more details. ## License diff --git a/bin/cmd.js b/bin/cmd.js index a1b1f2d..f7d58b5 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -1,7 +1,7 @@ #!/usr/bin/env node -//Starts budo with stdout -//Handles --help and error messaging -//Uses auto port-finding +// Starts budo with stdout +// Handles --help and error messaging +// Uses auto port-finding var args = process.argv.slice(2) -require('../').cli(args) \ No newline at end of file +require('../').cli(args) diff --git a/example/app.js b/example/app.js index 80459a7..20386f8 100644 --- a/example/app.js +++ b/example/app.js @@ -1,3 +1,4 @@ +/*globals Image*/ var dpr = window.devicePixelRatio || 1 var ctx = require('2d-context')() var fit = require('canvas-fit')(ctx.canvas, window, dpr) @@ -14,8 +15,8 @@ img.src = 'baboon.png' var time = 0 require('raf-loop')(function (dt) { - var width = ctx.canvas.width, - height = ctx.canvas.height + var width = ctx.canvas.width + var height = ctx.canvas.height ctx.clearRect(0, 0, width, height) time += dt / 1000 @@ -24,7 +25,8 @@ require('raf-loop')(function (dt) { ctx.scale(dpr, dpr) ctx.fillRect(Math.sin(time) * 50 + 300, 50, 20, 40) ctx.fillText('from browserify!', 40, 40) - if (img.width > 0 || img.height > 0) + if (img.width > 0 || img.height > 0) { ctx.drawImage(img, 50, 50) + } ctx.restore() }).start() diff --git a/example/other.js b/example/other.js index 49bb4af..4f3746e 100644 --- a/example/other.js +++ b/example/other.js @@ -1 +1 @@ -console.log("fas") \ No newline at end of file +console.log('fas') diff --git a/package.json b/package.json index a8c8545..dea323a 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "strip-ansi": "^3.0.0", "term-color": "^1.0.1", "tiny-lr": "^0.1.5", - "watchify-middleware": "^1.0.0", + "watchify-middleware": "^1.1.0", "xtend": "^4.0.0" }, "devDependencies": { @@ -45,15 +45,17 @@ "ndjson": "^1.4.1", "raf-loop": "^1.0.1", "request": "^2.53.0", + "standard": "^5.2.1", "tap-spec": "^3.0.0", "tape": "^4.0.0", + "through2": "^2.0.0", "tree-kill": "0.0.6", "uglify-js": "^2.4.19", "vm": "0.0.1", "win-spawn": "^2.0.0" }, "scripts": { - "test": "tape test/test*.js | tap-spec", + "test": "standard && tape test/test*.js | tap-spec", "budo-less": "node example/budo-less | garnish", "simple": "budo --dir example --live example/simple.js:bundle.js -- -t babelify" }, diff --git a/test/fixtures/app.js b/test/fixtures/app.js index 8bf4969..ea1edc4 100644 --- a/test/fixtures/app.js +++ b/test/fixtures/app.js @@ -1 +1 @@ -console.log('from browserify') \ No newline at end of file +console.log('from browserify') diff --git a/test/fixtures/first.js b/test/fixtures/first.js index 991eba2..efe794c 100644 --- a/test/fixtures/first.js +++ b/test/fixtures/first.js @@ -1 +1 @@ -global.start = 'foo' \ No newline at end of file +global.start = 'foo' diff --git a/test/fixtures/second.js b/test/fixtures/second.js index 056c32a..ddbb3b1 100644 --- a/test/fixtures/second.js +++ b/test/fixtures/second.js @@ -1,2 +1,2 @@ global.end = 'bar' -console.log(global.start+' '+global.end) \ No newline at end of file +console.log(global.start + ' ' + global.end) diff --git a/test/fixtures/with space.js b/test/fixtures/with space.js index bf71f46..2221b45 100644 --- a/test/fixtures/with space.js +++ b/test/fixtures/with space.js @@ -1 +1 @@ -console.log('with space') \ No newline at end of file +console.log('with space') diff --git a/test/test-bundle.js b/test/test-bundle.js index 0c4f5c4..bb807fc 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -4,7 +4,6 @@ var budo = require('../') var request = require('request') var xtend = require('xtend') var browserify = require('browserify') -var watchifyArgs = require('watchify').args var path = require('path') var vm = require('vm') @@ -85,19 +84,21 @@ function matches (t, entries, opt) { t.plan(shouldServe ? 5 : 4) var uri - if (!Array.isArray(entries)) + if (!Array.isArray(entries)) { entries = [ entries ] + } var app = budo(entries, opt) .on('connect', function (ev) { - if (shouldServe) + if (shouldServe) { t.equal(ev.serve, shouldServe, 'serves correct bundle file') + } uri = ev.uri + ev.serve t.ok(true, 'connected') }) .once('update', function () { - var b = browserify(xtend(watchifyArgs, { - debug: opt.debug, + var b = browserify(xtend({ + debug: opt.debug }, opt.browserify)) entries.forEach(function (entry) { entry = entry.split(':')[0] From dd05091d2fc290564bc946c8375eeceff20bfb6d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 19:01:40 -0400 Subject: [PATCH 153/302] doc changes and cleanup --- README.md | 22 ++++--- bin/cmd.js | 6 +- docs/{api.md => api-usage.md} | 76 ++++++++++++++--------- docs/{basics.md => command-line-usage.md} | 0 docs/errors.md | 30 --------- docs/tests-and-examples.md | 16 ++++- example/main.css | 2 +- lib/budo.js | 3 +- package.json | 4 +- 9 files changed, 87 insertions(+), 72 deletions(-) rename docs/{api.md => api-usage.md} (76%) rename docs/{basics.md => command-line-usage.md} (100%) delete mode 100644 docs/errors.md diff --git a/README.md b/README.md index 9f28996..9fc0f28 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,8 @@ See [docs](#docs) for more features. PRs/suggestions/comments welcome. ## docs -- [basic usage](docs/basics.md) -- [API and integrations (Gulp, Grunt, npm scripts)](docs/programmatic-usage.md) -- [error reporting](docs/errors.md) +- [command line usage](docs/command-line-usage.md) +- [API usage](docs/api-usage.md) - [running tests and examples](docs/tests-and-examples.md) - [rapid prototyping with budō](http://mattdesl.svbtle.com/rapid-prototyping) - [experimental script injection with budo-chrome](https://github.com/mattdesl/budo-chrome) @@ -94,18 +93,23 @@ The API mirrors the CLI except it does not write to `process.stdout` by default. ```js var budo = require('budo') +var babelify = require('babelify') budo('./src/index.js', { - live: true, // default live reload - stream: process.stdout, // log to stdout + live: true, // setup live reload port: 8000, // use this port - portfind: false // emit error if port is in use -}).on('connnect', function(ev) { - //... + browserify: { + transform: babelify // ES6 + } +}).on('connnect', function (ev) { + console.log('Server running on %s', ev.uri) + console.log('LiveReload running on port %s', ev.livePort) +}).on('update', function (buffer) { + console.log('bundle - %d bytes', buffer.length) }) ``` -See [API usage](docs/api-usage.md) for more details. +See [API usage](docs/api-usage.md) for details. ## License diff --git a/bin/cmd.js b/bin/cmd.js index f7d58b5..3176527 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -4,4 +4,8 @@ // Handles --help and error messaging // Uses auto port-finding var args = process.argv.slice(2) -require('../').cli(args) +require('../').cli(args, { + browserify: { + transform: require('babelify') + } +}) diff --git a/docs/api.md b/docs/api-usage.md similarity index 76% rename from docs/api.md rename to docs/api-usage.md index 52a1b0f..3588897 100644 --- a/docs/api.md +++ b/docs/api-usage.md @@ -2,30 +2,35 @@ The API mirrors the CLI except you must provide a `stream` for logging. -#### `b = budo(entry[, opts])` +### `b = budo([entry], [opts])` Sets up a new instance of `budo`, where `entry` is a path or or array of paths. -The options are the same as CLI, except that the API does not print to stdout. You can specify a `stream` option to print to. +`entry` can be optional -- if no entry paths are given, budo simply acts as a static HTTP server with optional LiveReload. The return value is an event emitter. -Example: +Examples: ```js var budo = require('budo') +var babelify = require('babelify') + budo('./src/index.js', { live: true, // live reload stream: process.stdout, // log to stdout - port: 8000 // use this as the base port + port: 8000, // use this as the base port + browserify: { + transform: babelify // use ES6 + } }).on('connnect', function(ev) { //... }) ``` -##### `opts` +#### `opts` -All options available through the API: +All settings are optional. - `port` (Number) - the base port to use for the development server (default `9966`) @@ -33,9 +38,10 @@ All options available through the API: - the base port to use for the LiveReload server (default `35729`) - `portfind` (Boolean) - whether to use portfinding to find the next available ports (default `true`) -- `host` (String) the host to listen on (default `'localhost'`) +- `host` (String) + - the host to listen on (default `'localhost'`) - `live` (Boolean|String) - - whether to set up a default LiveReload integration + - whether to set up a default LiveReload integration (see [LiveReload](#livereload)) - if a string is specified, only filenames matching that glob will trigger LiveReload events - `open` (Boolean) @@ -62,23 +68,28 @@ All options available through the API: - `pushstate` (Boolean) - enable push state support, which defaults 404 routes to the index (default `false`) -#### `b = budo.cli(args[, opts])` +### `b = budo.cli(args[, opts])` -Runs budo as a command-line tool, from the specified array of arguments and an optional `opts` object for overrides. For example: +Runs budo as a command-line tool, from the specified array of arguments and an optional `opts` object for overrides. The options are the same as above. -```js -var argv = require('minimist')(process.argv.slice(2), { - '--': true // allow full stop -}) +This method returns the `budo` instance, or `null` if the `args` command includes `--version` or `--help`. -var budo = require('budo')(argv, { - browserifyArgs: argv['--'] +For example, running the following script from the command line would behave like budo, but with some added features by default: + +```js +var args = process.argv.slice(2) +var babelify = require('babelify') + +var budo = require('budo')(args, { + // additional overrides for our custom tool + pushstate: true, + browserify: { + transform: babelify + } }) ``` -This method returns the `budo` instance, or `null` if the command is `--version` or `--help`. - -This will construct the browserify instance from the arguments, which supports the subarg syntax for plugins and transforms. +*Note:* In the CLI, anything after `--` gets passed to `opts.browserifyArgs`. Whenever `opts.browserifyArgs` is specified, browserify will be created with [browserify/bin/args](https://github.com/substack/node-browserify/blob/master/bin/args.js) instead of its usual constructor. #### `b.close()` @@ -98,7 +109,7 @@ If `live` was not specified, you can manually enable the LiveReload server with - `host` defaults to the `ev.host` from the `'connect'` event - `plugin` if true, the HTML will not have the LiveReload script injected into it -See [LiveReload](#LiveReload) for an example. +See [LiveReload](#livereload) for an example. #### `b.watch([globs, chokidarOpts])` @@ -106,7 +117,7 @@ If `live` was not specified, you can manually enabe [chokidar's](https://github. `globs` defaults to watching `**/*.{html,css}`. -See [LiveReload](#LiveReload) for an example. +See [LiveReload](#livereload) for an example. ## events @@ -164,9 +175,15 @@ If `opts.live` was not specified, and `b.watch()` was never set up, this event w #### LiveReload -Setting `opts.live` will provide a default configuration for live reloading. The API gives you more control over how and when to trigger reloads and watch events. +Setting `opts.live` will provide a default configuration for live reloading. You can also specify a string to narrow the LiveReload triggers to a certain glob: + +```js +budo('index.js', { + live: '*.{css,html}' +}) +``` -The following only triggers LiveReload events on CSS changes. +Using the `live()` and `watch()` methods instead of passing `opts.live`, you can fine-tune reloading for your use case. The following only triggers LiveReload events on CSS changes. ```js var budo = require('budo') @@ -174,20 +191,22 @@ var path = require('path') var app = budo('index.js') app - // listen to CSS changes with some chokidar options + // listen to CSS file changes with some chokidar options .watch('**/*.css', { interval: 300, usePolling: true }) // start LiveReload server .live() - // handle file events + // handle file changes .on('watch', function(type, file) { // tell LiveReload to inject some CSS if (path.extname(file) === '.css') { app.reload(file) } }) - .on('update', function (src) { - // log but don't trigger LiveReload - console.log('Bundle updated!') + .on('pending', function () { + console.log('bundle...') + }) + .on('update', function (buf) { + console.log('bundle finished --> %d bytes', buf.length) }) ``` @@ -204,6 +223,7 @@ var app = budo('./app.js', { res.statusCode = 200 res.end('hello world') } else { + // fall through to other budo routes next() } } diff --git a/docs/basics.md b/docs/command-line-usage.md similarity index 100% rename from docs/basics.md rename to docs/command-line-usage.md diff --git a/docs/errors.md b/docs/errors.md deleted file mode 100644 index 6be6519..0000000 --- a/docs/errors.md +++ /dev/null @@ -1,30 +0,0 @@ -# error reporting - -By default, bundle errors will be printed to `stdout` (terminal) and also printed to the browser console on reload[[1]](https://github.com/substack/watchify/blob/ffaf7ec048905f707ba1876579dc7082f1d50de5/bin/cmd.js#L27-L29) -. - -For clearer error reporting, you can use the [errorify](https://github.com/zertosh/errorify) plugin. This will write the error to the DOM with the problematic file and line number. - -```sh -# install -npm install errorify --save-dev -``` - -```json - "scripts": { - "start": "budo index.js --live -- -p errorify | garnish" - } -``` - -Using [babelify](https://www.npmjs.com/package/babelify) and errorify together, the error message looks like this: - -![enhanced](http://i.imgur.com/Q4DLQBQ.png) - -It uses the following CSS style: - -```css -body > .errorify { - color: red; - padding: 5px 10px; -} -``` diff --git a/docs/tests-and-examples.md b/docs/tests-and-examples.md index 22cb209..1ceeab4 100644 --- a/docs/tests-and-examples.md +++ b/docs/tests-and-examples.md @@ -38,4 +38,18 @@ To run the example with live reloading: npm run live ``` -Again, open `localhost:9966` and try making changes to `example/app.js`, `example/index.html` or `example/theme.css`. The CSS should be injected without a page refresh, and HTML/JS content will trigger a page reload. \ No newline at end of file +Again, open `localhost:9966` and try making changes to `example/app.js`, `example/index.html` or `example/theme.css`. The CSS should be injected without a page refresh, and HTML/JS content will trigger a page reload. + +#### budo-less + +An example of LESS recompilation on the fly, without writing to a file. + +```sh +npm run budo-less +``` + +Now open `localhost:9966` and try making changes to `example/style.less` (with LESS syntax). On file save, it will re-request `main.css`. The middleware function compiles the LESS file on the fly and writes CSS as the server response. + +The benefit of this approach is that the development and production build can use the same `index.html`, since the paths do not need to change. This is particularly useful for GitHub demos. + +See `examples/budo-less.js` for the build script. \ No newline at end of file diff --git a/example/main.css b/example/main.css index a0e303f..c489d30 100644 --- a/example/main.css +++ b/example/main.css @@ -1,3 +1,3 @@ body { - background: #efefef; + background: red; } \ No newline at end of file diff --git a/lib/budo.js b/lib/budo.js index a7e373d..2a77753 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -140,7 +140,8 @@ function createBudo (entries, opts) { } else { // destroy previous if (fileWatcher) fileWatcher.close() - glob = glob && glob.length > 0 ? glob : [ '**/*.{html,css}' ] + glob = glob && glob.length > 0 ? glob : '**/*.{html,css}' + glob = Array.isArray(glob) ? glob : [ glob ] watchOpt = xtend({ poll: opts.poll }, watchOpt) fileWatcher = createFileWatch(glob, watchOpt) diff --git a/package.json b/package.json index dea323a..d703840 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,9 @@ "scripts": { "test": "standard && tape test/test*.js | tap-spec", "budo-less": "node example/budo-less | garnish", - "simple": "budo --dir example --live example/simple.js:bundle.js -- -t babelify" + "simple": "./bin/cmd.js --dir example --live example/simple.js:bundle.js -- -t babelify", + "start": "./bin/cmd.js example/app.js:bundle.js --dir example | garnish", + "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live | garnish" }, "keywords": [ "browserify", From c91fe56243ff089cdb3d2a111b6c4cfb4c214136 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 19:13:14 -0400 Subject: [PATCH 154/302] babelify for simple example and docs updates --- README.md | 16 +++++++++++++++- docs/tests-and-examples.md | 4 +++- example/simple.js | 6 ++++-- package.json | 3 ++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9fc0f28..6df5620 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,21 @@ Result:
-See [docs](#docs) for more features. PRs/suggestions/comments welcome. +See [docs](#docs) for more details. PRs/suggestions/comments welcome. + +## features + +At a glance: + +- stubs a default `index.html` +- fast incremental bundling, suspending the response until the new source is ready +- watches HTML and CSS files for changes; CSS is injected without reloading the page +- allows for detailed logging with [garnish](https://github.com/mattdesl/garnish) +- provides clear error messaging during development in DOM and console + +Below is an example of how syntax errors look during development, using the [babelify](https://github.com/babel/babelify) transform. + +![babelify](http://i.imgur.com/fDAKMHE.png) ## docs diff --git a/docs/tests-and-examples.md b/docs/tests-and-examples.md index 1ceeab4..d8d8aa2 100644 --- a/docs/tests-and-examples.md +++ b/docs/tests-and-examples.md @@ -18,7 +18,9 @@ Now you can run the following to see the unit tests: npm test ``` -## running the example +## running the examples + +See the [package.json](../package.json) `"scripts"` field for how these work. #### basic diff --git a/example/simple.js b/example/simple.js index 996ce85..b206f42 100644 --- a/example/simple.js +++ b/example/simple.js @@ -1,2 +1,4 @@ -var res = require('url').parse(window.location.href) -console.log(res) +// npm run es6 + +import { parse } from 'url' +console.log(parse(window.location.href)) diff --git a/package.json b/package.json index d703840..152b247 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "2d-context": "^1.2.0", + "babelify": "^6.3.0", "brfs": "^1.4.0", "browserify": "^9.0.8", "canvas-fit": "^1.2.0", @@ -57,7 +58,7 @@ "scripts": { "test": "standard && tape test/test*.js | tap-spec", "budo-less": "node example/budo-less | garnish", - "simple": "./bin/cmd.js --dir example --live example/simple.js:bundle.js -- -t babelify", + "es6": "./bin/cmd.js --dir example --live example/simple.js:bundle.js -- -t babelify", "start": "./bin/cmd.js example/app.js:bundle.js --dir example | garnish", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live | garnish" }, From d19d3cd097cdf53d04e12d5fa184d0f41cc888ab Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 19:14:33 -0400 Subject: [PATCH 155/302] 5.0.0-beta --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 152b247..2341ba1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "4.2.1", + "version": "5.0.0-beta", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 0938e878d92f1d2f5e4521a9b5f4a84f61c2c2ae Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 19:19:16 -0400 Subject: [PATCH 156/302] updating readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 9534747..cf96a2d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ +#### 5.0.0-beta + +budo@5 is in beta, you can test it like so: + +```sh +# install the latest version of budo & garnish +npm install budo@next garnish -g + +# run the tools +budo src/index.js | garnish +``` + +More details and docs in the [next](https://github.com/mattdesl/budo/tree/next) branch. + # budō [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) From ee41c10f70dca59c3edb30fbd646368d0af2fcae Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Sep 2015 19:23:48 -0400 Subject: [PATCH 157/302] updating readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 6df5620..bc14fea 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +#### beta + +budo@5 is still in beta, you can test it like so: + +```sh +# install the latest version of budo & garnish +npm install budo@next garnish -g + +# run the tools +budo src/index.js | garnish +``` + # budō [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) From 9e417871966674b4498234117b3722b184ada3a7 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 9 Sep 2015 11:25:18 -0400 Subject: [PATCH 158/302] remove problematic debugging code --- bin/cmd.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/cmd.js b/bin/cmd.js index 3176527..f7d58b5 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -4,8 +4,4 @@ // Handles --help and error messaging // Uses auto port-finding var args = process.argv.slice(2) -require('../').cli(args, { - browserify: { - transform: require('babelify') - } -}) +require('../').cli(args) From c4642ede4d677b2ee943f71ca443b40108ca3b92 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 9 Sep 2015 11:25:28 -0400 Subject: [PATCH 159/302] 5.0.0-beta1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2341ba1..161a6e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0-beta", + "version": "5.0.0-beta1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 0727998c8e3818fe38faf1b9bed739995a11787f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 9 Sep 2015 15:17:16 -0400 Subject: [PATCH 160/302] documenting other modules, using new colors for error messages --- README.md | 8 ++++++++ lib/error-handler.js | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc14fea..a666d3e 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,14 @@ budo('./src/index.js', { See [API usage](docs/api-usage.md) for details. +## See Also + +budō combines several smaller and less opinionated modules. + +- [watchify-middleware](https://www.npmjs.com/package/watchify-middleware) - the underlying request handler for serving incremental reloads +- [watchify-server](https://www.npmjs.com/package/watchify-server) - a less opinionated alternative to budo, built on the same underlying modules +- [simple-html-index](https://www.npmjs.com/package/simple-html-index) - a stream for a default `index.html` file + ## License MIT, see [LICENSE.md](http://github.com/mattdesl/budo/blob/master/LICENSE.md) for details. \ No newline at end of file diff --git a/lib/error-handler.js b/lib/error-handler.js index 6f08d1a..d8e2afd 100644 --- a/lib/error-handler.js +++ b/lib/error-handler.js @@ -15,7 +15,7 @@ function bundleError (message) { pre.textContent = message var style = { position: 'fixed', - background: '#efefef', + background: '#fff', width: '100%', zIndex: '100000', height: '100%', @@ -26,7 +26,7 @@ function bundleError (message) { 'word-wrap': 'break-word', 'font-size': '16px', margin: '0', - color: '#C71A1A' + color: '#c71a1a' } Object.keys(style).forEach(function (k) { From 6372153e443ed707f5556dac04d2801481a670e1 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 9 Sep 2015 15:43:34 -0400 Subject: [PATCH 161/302] testing onupdate exec events --- index.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index df99ca9..0a86a43 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ var parseArgs = require('./lib/parse-args') var budo = require('./lib/budo') var color = require('term-color') +var exec = require('child_process').exec module.exports = budo module.exports.cli = budoCLI @@ -42,7 +43,21 @@ function budoCLI (args, opts) { argv.live = argv.live === 'true' } - return budo(entries, argv).on('error', exit) + var instance = budo(entries, argv).on('error', exit) + var onUpdates = [].concat(argv.onupdate).filter(Boolean) + onUpdates.forEach(function (cmd) { + instance.on('update', execFunc(cmd)) + }) + + return instance +} + +function execFunc (cmd) { + return function run () { + var p = exec(cmd) + p.stderr.pipe(process.stderr) + p.stdout.pipe(process.stdout) + } } function exit (err) { From 1f3c9e721d43531cf37411e56847fcd7ff012852 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 10 Sep 2015 12:52:34 -0400 Subject: [PATCH 162/302] bundle times above 1 second show as yellow with garnish --- lib/budo.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/budo.js b/lib/budo.js index 2a77753..5094a8b 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -70,7 +70,11 @@ function createBudo (entries, opts) { if (ev.type === 'bundle') { ev.url = '/' + opts.serve ev.name = 'browserify' - ev.elapsed = ev.elapsed + 'ms' + var time = ev.elapsed + ev.elapsed = time + ' ms' + if (time > 1000) { // yellow for longer bundle time + ev.colors = { elapsed: 'yellow' } + } } log.info(ev) }) From dc21bfe129e447e003791c7caf6c9ddd9c622c24 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 10 Sep 2015 13:10:36 -0400 Subject: [PATCH 163/302] use hard red and white background --- README.md | 2 +- lib/error-handler.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a666d3e..8ad6c0e 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ At a glance: Below is an example of how syntax errors look during development, using the [babelify](https://github.com/babel/babelify) transform. -![babelify](http://i.imgur.com/fDAKMHE.png) +![babelify](http://i.imgur.com/ZlfJL1i.png) ## docs diff --git a/lib/error-handler.js b/lib/error-handler.js index d8e2afd..04becba 100644 --- a/lib/error-handler.js +++ b/lib/error-handler.js @@ -15,7 +15,6 @@ function bundleError (message) { pre.textContent = message var style = { position: 'fixed', - background: '#fff', width: '100%', zIndex: '100000', height: '100%', @@ -26,7 +25,8 @@ function bundleError (message) { 'word-wrap': 'break-word', 'font-size': '16px', margin: '0', - color: '#c71a1a' + background: '#fff', // or ffefef ? + color: '#ff0000' } Object.keys(style).forEach(function (k) { From a259374f1fd10bde677d3824db3df4563c3546f0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 10 Sep 2015 13:10:59 -0400 Subject: [PATCH 164/302] 5.0.0-beta2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 161a6e3..30aef36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0-beta1", + "version": "5.0.0-beta2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From c3c6f2d505ea9b9d6bb708db4266dcbeb6d56463 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 12 Sep 2015 13:39:51 -0400 Subject: [PATCH 165/302] add test for defaultIndex --- test/test-server.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/test-server.js b/test/test-server.js index c5fa490..a780db3 100644 --- a/test/test-server.js +++ b/test/test-server.js @@ -1,5 +1,6 @@ var test = require('tape') var budo = require('../') +var defaultHtml = require('simple-html-index') var request = require('request') var entry = 'test/fixtures/app.js' @@ -25,6 +26,34 @@ test('should serve on --dir', function (t) { .on('error', t.fail.bind(t)) }) +test('support defaultIndex stream', function (t) { + t.plan(3) + + function html (opt) { + return defaultHtml({ + entry: opt.entry, + title: 'foobar', + css: 'main.css' + }) + } + + var app = budo(entry, { dir: __dirname, defaultIndex: html }) + .on('connect', function (ev) { + request.get({ + uri: ev.uri + }, function (err, resp, body) { + if (err) t.fail(err) + t.equal(resp.statusCode, 200) + t.equal(body, 'foobar') + app.close() + }) + }) + .on('exit', function () { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) +}) + test('favicon.ico should have status code 200', function (t) { t.plan(2) var app = budo(entry, { dir: __dirname }) From 29fad6d2bf803c12c34b0dcc09947d71a29ce344 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 12 Sep 2015 13:42:22 -0400 Subject: [PATCH 166/302] document defaultIndex --- docs/api-usage.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/api-usage.md b/docs/api-usage.md index 3588897..457fd66 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -1,6 +1,6 @@ # API -The API mirrors the CLI except you must provide a `stream` for logging. +The API mirrors the CLI except you must provide a `stream` for logging, and it provides a couple extra options. ### `b = budo([entry], [opts])` @@ -67,6 +67,9 @@ All settings are optional. - can be a `reporter(err)` function which takes an Error and returns the new bundle contents - `pushstate` (Boolean) - enable push state support, which defaults 404 routes to the index (default `false`) +- `defaultIndex` (Function) + - a function `fn(opt)` that returns a Readable stream, takes parameter `{ entry: opts.serve }` + - defaults to [simple-html-index](https://github.com/mattdesl/simple-html-index) ### `b = budo.cli(args[, opts])` From 8a726d9f487163265d3505f3f0c37ad6a7ebe677 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 12 Sep 2015 16:23:16 -0400 Subject: [PATCH 167/302] if middleware has an error, report it with status code 400 --- lib/server.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index fee5692..e056be8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -33,9 +33,16 @@ function createServer (entryHandler, opts) { if (middleware.length === 3) { var removeLogger = logger('middleware', req, res) // user wants to specify which routes to fall through to budo - middleware(req, res, function () { + middleware(req, res, function (err) { removeLogger() - router(req, res) + if (err) { + var msg = err.message ? err.message : err + console.error(msg) + res.statusCode = 400 + res.end(msg) + } else { + router(req, res) + } }) } else { // all routes will fall through to budo routes From 0d6010ce5184a2307f064cc9e17e3b07b6350a1e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 12 Sep 2015 16:24:08 -0400 Subject: [PATCH 168/302] 5.0.0-beta3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30aef36..d955d03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0-beta2", + "version": "5.0.0-beta3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 54813adeecb5bd3009ef21ea3f63399f48e05935 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sun, 13 Sep 2015 14:20:48 -0400 Subject: [PATCH 169/302] no longer need to worry about conflicts because of full-stop in args --- index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.js b/index.js index df99ca9..8f803ab 100644 --- a/index.js +++ b/index.js @@ -33,8 +33,6 @@ function budoCLI (args, opts) { if (argv.outfile) { console.error(color.yellow('WARNING'), '--outfile has been removed in budo@3.0') - // ensure we don't pass to watchify - delete argv.outfile } // opts.live can be a glob or a boolean From be16a09dae438d45175627c7a0de00f66759ba14 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 14 Sep 2015 07:35:11 -0400 Subject: [PATCH 170/302] changing styling; removing live update logs since they clutter things; using dim for timing --- lib/budo.js | 6 ++---- lib/server.js | 9 ++++++++- lib/tinylr.js | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 5094a8b..f2b9c1a 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -71,10 +71,8 @@ function createBudo (entries, opts) { ev.url = '/' + opts.serve ev.name = 'browserify' var time = ev.elapsed - ev.elapsed = time + ' ms' - if (time > 1000) { // yellow for longer bundle time - ev.colors = { elapsed: 'yellow' } - } + ev.elapsed = time + 'ms' + ev.colors = { elapsed: time > 1000 ? 'yellow' : 'dim' } } log.info(ev) }) diff --git a/lib/server.js b/lib/server.js index e056be8..bbbc106 100644 --- a/lib/server.js +++ b/lib/server.js @@ -72,13 +72,20 @@ function createServer (entryHandler, opts) { return server function logger (type, req, res) { + var now = Date.now() var fn = function () { + var elapsed = Date.now() - now log.info({ + elapsed: elapsed + 'ms', name: 'http', message: (req.method || 'GET').toUpperCase(), url: req.url, statusCode: res.statusCode, - type: type + type: type, + colors: { + elapsed: elapsed > 1000 ? 'yellow' : 'dim', + message: 'dim' + } }) } res.once('finish', fn) diff --git a/lib/tinylr.js b/lib/tinylr.js index 53fad3c..c6769d7 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -39,7 +39,8 @@ module.exports = function (opt) { close: close, reload: function reload (path) { - log.info({ name: 'live', url: path, type: 'reload' }) + // this starts to clutter things a bit ... + // log.info({ name: 'live', url: path, type: 'reload' }) try { server.changed({ body: { From 3c6596bcc12bc783d685624b4dcaa3b006b8393d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 18 Sep 2015 15:50:28 -0400 Subject: [PATCH 171/302] add error to tinylr --- lib/tinylr.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/tinylr.js b/lib/tinylr.js index c6769d7..1553b8a 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -25,8 +25,10 @@ module.exports = function (opt) { serverImpl.on('error', function (err) { if (err.code === 'EADDRINUSE') { process.stderr.write('ERROR: livereload not started, port ' + opt.port + ' is in use\n') - close() + } else { + process.stderr.write((err.stack ? err.stack : err) + '\n') } + close() }) function close () { From 27cb6ae98abbcfaac60ba815beed565350ad8733 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 18 Sep 2015 15:57:54 -0400 Subject: [PATCH 172/302] outdated dependencies updated --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d955d03..da4461a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "bole": "^2.0.0", "browserify": "^11.0.1", "chokidar": "^1.0.1", - "ecstatic": "^0.7.2", + "ecstatic": "^1.0.0", "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", @@ -50,7 +50,7 @@ "tap-spec": "^3.0.0", "tape": "^4.0.0", "through2": "^2.0.0", - "tree-kill": "0.0.6", + "tree-kill": "^0.1.1", "uglify-js": "^2.4.19", "vm": "0.0.1", "win-spawn": "^2.0.0" From a5915dd27413124e27fbbf966ca65a54cd35e04a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 19 Sep 2015 09:44:27 -0400 Subject: [PATCH 173/302] remove vm dep --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index da4461a..399d3bf 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "through2": "^2.0.0", "tree-kill": "^0.1.1", "uglify-js": "^2.4.19", - "vm": "0.0.1", "win-spawn": "^2.0.0" }, "scripts": { From f8b1966d385fa48ecb08644dc749c3de10dc1c90 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 19 Sep 2015 10:37:37 -0400 Subject: [PATCH 174/302] add gotcha about body tag --- docs/command-line-usage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index 4a727d3..e28a40a 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -82,6 +82,8 @@ From the command line, you can specify a filename glob to only trigger LiveReloa budo index.js --live=*.{html,css} ``` +*Note:* Your `index.html` must have a `` tag for the LiveReload script to get injected! + ## multiple entries Budo also supports multiple entry points; they will all get concatenated into a single bundle. If you aren't using a colon separator (`:`), the entry point will default to the first path. Or, you can explicitly set the path with the `--serve` option, as below: From 04bafec58adabeb0746117bad32a1fa403aff52e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 19 Sep 2015 13:12:29 -0400 Subject: [PATCH 175/302] listening for bundle errors now; experimental onerror flag --- docs/command-line-usage.md | 10 ++++++++++ errscript.js | 10 ++++++++++ index.js | 17 ++++++++++++++--- lib/budo.js | 1 + lib/error-handler.js | 1 + lib/parse-args.js | 2 ++ test/test-onupdate.js | 17 +++++++++++++++++ 7 files changed, 55 insertions(+), 3 deletions(-) create mode 100755 errscript.js create mode 100644 test/test-onupdate.js diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index 4a727d3..dd762a7 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -112,6 +112,16 @@ budo index.js --open | garnish Also see [opnr](https://github.com/mattdesl/opnr), which allows for a similar functionality without forcing it as a command-line flag. +## `--onupdate` + +In the CLI, you can run shell commands when the bundle updates using the `--onupdate` option. For example, to lint with [standard](https://github.com/feross/standard): + +```sh +budo index.js --onupdate standard | garnish +``` + +This flag is only available in the command-line. + ## internal IP If you want LiveReload to work across many devices, you need to use your internal IP rather than `localhost`. diff --git a/errscript.js b/errscript.js new file mode 100755 index 0000000..068df46 --- /dev/null +++ b/errscript.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +// console.log("HELLO") +// process.stdout.write('beep!\x07\n') +// console.log("TTY", process.stdin.isTTY) +// process.stdin.on('data', function (data) { +// console.log('data',data.toString()) +// }) +require('get-stdin')().then(function (msg) { + console.log("ERR: '" + msg.toString() + "'") +}) \ No newline at end of file diff --git a/index.js b/index.js index 0a86a43..d7aba63 100644 --- a/index.js +++ b/index.js @@ -43,20 +43,31 @@ function budoCLI (args, opts) { argv.live = argv.live === 'true' } + // CLI only option for executing a child process var instance = budo(entries, argv).on('error', exit) var onUpdates = [].concat(argv.onupdate).filter(Boolean) onUpdates.forEach(function (cmd) { instance.on('update', execFunc(cmd)) }) - + + // this feature is experimental and may be subject to removal + var onErrors = [].concat(argv.onerror).filter(Boolean) + onErrors.forEach(function (cmd) { + instance.on('bundle-error', execFunc(cmd, true)) + }) + return instance } -function execFunc (cmd) { - return function run () { +function execFunc (cmd, isErr) { + return function run (err) { var p = exec(cmd) p.stderr.pipe(process.stderr) p.stdout.pipe(process.stdout) + if (isErr && err && err.message) { + p.stdin.write(err.message.trim() + '\n') + p.stdin.end() + } } } diff --git a/lib/budo.js b/lib/budo.js index 2a77753..22f2c45 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -80,6 +80,7 @@ function createBudo (entries, opts) { bundler.on('error', function (err) { console.error('Error:', err.message ? err.message : err) }) + bundler.on('bundle-error', emitter.emit.bind(emitter, 'bundle-error')) bundler.on('update', emitter.emit.bind(emitter, 'update')) bundler.on('pending', emitter.emit.bind(emitter, 'pending')) } diff --git a/lib/error-handler.js b/lib/error-handler.js index d8e2afd..163dab0 100644 --- a/lib/error-handler.js +++ b/lib/error-handler.js @@ -1,6 +1,7 @@ var stripAnsi = require('strip-ansi') module.exports = errorHandler + function bundleError (message) { console.error(message) if (typeof document === 'undefined') { diff --git a/lib/parse-args.js b/lib/parse-args.js index 52f16f8..36bb3b7 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -16,6 +16,8 @@ function parseArgs (args, opt) { 'host', 'port', 'dir', + 'onupdate', + 'onerror', 'serve' ], default: { diff --git a/test/test-onupdate.js b/test/test-onupdate.js new file mode 100644 index 0000000..bff3c8d --- /dev/null +++ b/test/test-onupdate.js @@ -0,0 +1,17 @@ +var test = require('tape') +var path = require('path') +var kill = require('tree-kill') + +var spawn = require('win-spawn') +var cli = path.resolve(__dirname, '..', 'bin', 'cmd.js') + +test('should trigger echo', function (t) { + t.plan(1) + var src = path.resolve(__dirname, 'fixtures', 'app.js') + var proc = spawn(cli, [ src, '--onupdate', 'echo FOO BAR', '--no-stream' ]) + + proc.stdout.on('data', function (buf) { + t.equal(buf.toString(), 'FOO BAR\n') + kill(proc.pid) + }) +}) From 10de3d6404f56acf61e53e05987b910641960dac Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 19 Sep 2015 13:25:55 -0400 Subject: [PATCH 176/302] remove experimental onerror feature; add docs on update feature --- docs/command-line-usage.md | 10 +++++++--- index.js | 14 ++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index dd762a7..4d092d9 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -114,13 +114,17 @@ Also see [opnr](https://github.com/mattdesl/opnr), which allows for a similar fu ## `--onupdate` -In the CLI, you can run shell commands when the bundle updates using the `--onupdate` option. For example, to lint with [standard](https://github.com/feross/standard): +In the CLI, you can run shell commands when the bundle updates using the `--onupdate` option. For example, to lint with [standard](https://github.com/feross/standard) and provide an alert with [notify-error](https://github.com/mattdesl/notify-error): ```sh -budo index.js --onupdate standard | garnish +budo index.js --onupdate "standard | notify-error" ``` -This flag is only available in the command-line. +Now, when you save the bundle, `standard` will run on your directory. If lint errors are found, they will print to the console and show an alert notification: + + + +The flag is only available in the command-line. ## internal IP diff --git a/index.js b/index.js index d7aba63..fc4efae 100644 --- a/index.js +++ b/index.js @@ -50,24 +50,14 @@ function budoCLI (args, opts) { instance.on('update', execFunc(cmd)) }) - // this feature is experimental and may be subject to removal - var onErrors = [].concat(argv.onerror).filter(Boolean) - onErrors.forEach(function (cmd) { - instance.on('bundle-error', execFunc(cmd, true)) - }) - return instance } -function execFunc (cmd, isErr) { - return function run (err) { +function execFunc (cmd) { + return function run () { var p = exec(cmd) p.stderr.pipe(process.stderr) p.stdout.pipe(process.stdout) - if (isErr && err && err.message) { - p.stdin.write(err.message.trim() + '\n') - p.stdin.end() - } } } From 11ec30fba58fe2de3d29b351e431e560bff756cf Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 19 Sep 2015 13:27:15 -0400 Subject: [PATCH 177/302] remove unused file; fix parse args for onupdate --- errscript.js | 10 ---------- lib/parse-args.js | 1 - 2 files changed, 11 deletions(-) delete mode 100755 errscript.js diff --git a/errscript.js b/errscript.js deleted file mode 100755 index 068df46..0000000 --- a/errscript.js +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env node -// console.log("HELLO") -// process.stdout.write('beep!\x07\n') -// console.log("TTY", process.stdin.isTTY) -// process.stdin.on('data', function (data) { -// console.log('data',data.toString()) -// }) -require('get-stdin')().then(function (msg) { - console.log("ERR: '" + msg.toString() + "'") -}) \ No newline at end of file diff --git a/lib/parse-args.js b/lib/parse-args.js index 36bb3b7..e45abff 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -17,7 +17,6 @@ function parseArgs (args, opt) { 'port', 'dir', 'onupdate', - 'onerror', 'serve' ], default: { From fe63e5327be1aafaee06239721dcd2ee3c955505 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 19 Sep 2015 19:48:37 +0200 Subject: [PATCH 178/302] fix syntax-highlight of CLI usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf96a2d..6245993 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ See [docs](#docs) for more features. PRs/suggestions/comments welcome. Details for `budo` command-line interface. Other options (like `-t`) will be sent to browserify. -```sh +```txt Usage: budo [entries] [opts] From 2c14d7a73a9c410fd16c4f213571d5f551f0cbbc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Sep 2015 18:45:02 -0400 Subject: [PATCH 179/302] fix livereload script for npm@3 and remove vm --- lib/tinylr.js | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/tinylr.js b/lib/tinylr.js index 21c268a..35f06ae 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -9,7 +9,10 @@ module.exports = function(opt) { if (typeof opt.port !== 'number') opt.port = 35729 - var server = tinylr() + var server = tinylr({ + livereload: require.resolve('livereload-js/dist/livereload.js') + }) + var closed = false, ready = false server.listen(opt.port, opt.host, function() { diff --git a/package.json b/package.json index eeab3b6..fe03e55 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", + "livereload-js": "^2.2.2", "minimist": "^1.1.0", "resolve": "^1.1.6", "routes-router": "^4.1.2", @@ -44,7 +45,6 @@ "tape": "^4.0.0", "tree-kill": "0.0.6", "uglify-js": "^2.4.19", - "vm": "0.0.1", "win-spawn": "^2.0.0" }, "scripts": { From a4bb49e64a42efda73c6f1a3e28f42e1c461920d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Sep 2015 18:45:32 -0400 Subject: [PATCH 180/302] 4.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe03e55..330a7fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "4.2.1", + "version": "4.2.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From fdc58f189a7f6877c50816926c83433282f52a07 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Sep 2015 18:49:40 -0400 Subject: [PATCH 181/302] 5.0.0-beta4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0421b35..683c322 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0-beta3", + "version": "5.0.0-beta4", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From c9ffb2606434c0c9a57368b2abd12b3252d563d5 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Sep 2015 19:00:06 -0400 Subject: [PATCH 182/302] update cli args --- README.md | 1 + bin/help.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 508133a..cd146e3 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ Options: --live-port, -L the LiveReload port, default 35729 --open, -o launch the browser once connected --pushstate, -P always render the index page instead of a 404 page + --onupdate a shell command to trigger on bundle update --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout --no-debug do not use inline source maps diff --git a/bin/help.txt b/bin/help.txt index 2fab944..6d42fd3 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -12,6 +12,7 @@ Options: --live-port, -L the LiveReload port, default 35729 --open, -o launch the browser once connected --pushstate, -P always render the index page instead of a 404 page + --onupdate a shell command to trigger on bundle update --poll=N use polling for file watch, with optional interval N --no-stream do not print messages to stdout --no-debug do not use inline source maps From 573577d3b7ed81224341d9d5ea6b47e0a0f45809 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 24 Sep 2015 10:23:12 -0400 Subject: [PATCH 183/302] fix bug in onupdate test not closing; use internal-ip by default when no host is given --- index.js | 2 +- lib/budo.js | 17 +++++++++++++---- package.json | 3 ++- test/test-api.js | 32 +++++++++++++++++++++++++++++++- test/test-live.js | 3 +++ test/test-onupdate.js | 6 +++++- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index a3f65cf..708ed22 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,7 @@ function budoCLI (args, opts) { onUpdates.forEach(function (cmd) { instance.on('update', execFunc(cmd)) }) - + return instance } diff --git a/lib/budo.js b/lib/budo.js index a64f9c1..b1438fe 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -5,6 +5,7 @@ var path = require('path') var EventEmitter = require('events').EventEmitter var isMatch = require('micromatch').isMatch var openUrl = require('opn') +var internalIp = require('internal-ip') var getPorts = require('./get-ports') var createServer = require('./server') @@ -58,7 +59,6 @@ function createBudo (entries, opts) { opts.dir = opts.cwd } - var hostname = (opts.host || 'localhost') var emitter = new EventEmitter() var bundler, middleware @@ -194,15 +194,24 @@ function createBudo (entries, opts) { }) // start server - server.listen(opts.port, opts.host, connect) + // no host -> use localhost + internal-ip + server.listen(opts.port, opts.host || undefined, connect) } function connect () { if (closed) return started = true + // if no host is specified, use internal IP + if (!opts.host) { + opts.host = server.address().address + if (opts.host === '::') { + opts.host = internalIp() + } + } + var port = opts.port - var uri = 'http://' + hostname + ':' + port + '/' + var uri = 'http://' + opts.host + ':' + port + '/' log.info({ message: 'Server running at', url: uri, type: 'connect' }) @@ -215,7 +224,7 @@ function createBudo (entries, opts) { uri: uri, port: port, livePort: opts.livePort, - host: hostname, + host: opts.host, serve: opts.serve, entries: entryFiles, dir: opts.dir diff --git a/package.json b/package.json index 683c322..6422972 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "events": "^1.0.2", "getport": "^0.1.0", "inject-lr-script": "^1.0.0", + "internal-ip": "^1.0.1", "livereload-js": "^2.2.2", "micromatch": "^2.2.0", "minimist": "^1.1.0", @@ -41,7 +42,7 @@ "browserify": "^9.0.8", "canvas-fit": "^1.2.0", "domready": "^1.0.7", - "garnish": "^3.0.0", + "garnish": "^3.2.1", "inject-lr-script": "^1.0.1", "less-css-stream": "^1.0.0", "ndjson": "^1.4.1", diff --git a/test/test-api.js b/test/test-api.js index f98905f..f1ba4f1 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -1,6 +1,35 @@ var test = require('tape') var budo = require('../') var through = require('through2') +var internalIp = require('internal-ip') + +test('uses internal IP when no host is given', function (t) { + t.plan(3) + t.timeoutAfter(10000) + var internal = internalIp() + + var app = budo('test/fixtures/app.js', { + dir: __dirname, + port: 8000 + }) + .on('error', function (err) { + t.fail(err) + }) + .on('connect', function (ev) { + t.equal(ev.uri, 'http://' + internal + ':8000/', 'uri matches') + t.equal(ev.host, internal, 'host defaults to internal ip') + app.close() + }) + .on('reload', function () { + t.fail('should not have received reload event') + }) + .on('watch', function () { + t.fail('should not have received watch event') + }) + .on('exit', function () { + t.ok(true, 'closing') + }) +}) test('gets connect info', function (t) { t.plan(7) @@ -8,6 +37,7 @@ test('gets connect info', function (t) { var app = budo('test/fixtures/app.js', { dir: __dirname, + host: 'localhost', port: 8000 }) .on('error', function (err) { @@ -17,7 +47,7 @@ test('gets connect info', function (t) { t.deepEqual(ev.entries, [ 'test/fixtures/app.js' ], 'entries matches') t.equal(ev.serve, 'app.js', 'mapping matches') t.equal(ev.uri, 'http://localhost:8000/', 'uri matches') - t.equal(ev.host, 'localhost', 'host is not specified') + t.equal(ev.host, 'localhost', 'host is specified') t.equal(ev.port, 8000, 'port matches') t.equal(ev.dir, __dirname, 'dir matches') app.close() diff --git a/test/test-live.js b/test/test-live.js index 2fcd3a1..25b3a8a 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -13,6 +13,7 @@ test('should inject LiveReload snippet', function (t) { var app = budo(entry, { dir: __dirname, port: 8000, + host: 'localhost', serve: 'app.js', live: true }) @@ -45,6 +46,7 @@ test('manual LiveReload triggering', function (t) { var app = budo(entry, { dir: __dirname, port: 8000, + host: 'localhost', serve: 'app.js' }) .watch() @@ -79,6 +81,7 @@ test('should not inject LiveReload snippet', function (t) { var app = budo(entry, { dir: __dirname, port: 8000, + host: 'localhost', serve: 'app.js' }) .live({ plugin: true }) diff --git a/test/test-onupdate.js b/test/test-onupdate.js index bff3c8d..c8fb40e 100644 --- a/test/test-onupdate.js +++ b/test/test-onupdate.js @@ -6,12 +6,16 @@ var spawn = require('win-spawn') var cli = path.resolve(__dirname, '..', 'bin', 'cmd.js') test('should trigger echo', function (t) { - t.plan(1) + t.plan(2) + t.timeoutAfter(5000) var src = path.resolve(__dirname, 'fixtures', 'app.js') var proc = spawn(cli, [ src, '--onupdate', 'echo FOO BAR', '--no-stream' ]) proc.stdout.on('data', function (buf) { t.equal(buf.toString(), 'FOO BAR\n') + proc.on('exit', function () { + t.ok(true, 'closed') + }) kill(proc.pid) }) }) From 747eb61d0fd2227af7a784c1d2b73fadb3166bfb Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 24 Sep 2015 10:24:21 -0400 Subject: [PATCH 184/302] add open to start script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6422972..92f02da 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "test": "standard && tape test/test*.js | tap-spec", "budo-less": "node example/budo-less | garnish", "es6": "./bin/cmd.js --dir example --live example/simple.js:bundle.js -- -t babelify", - "start": "./bin/cmd.js example/app.js:bundle.js --dir example | garnish", + "start": "./bin/cmd.js example/app.js:bundle.js --dir example --open | garnish", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live | garnish" }, "keywords": [ From 436dad7237ae243759f88b4e3e60155ef61a1f10 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 24 Sep 2015 10:38:57 -0400 Subject: [PATCH 185/302] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bf5e11..68a223f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ##### Major Changes +- if no `--host` is specified, resolves to internal IP + - you can still hit `localhost:9966` and it will work - the `' +test('accept "." entry point', function (t) { + // currently we can't test budo('.') because the index.js + // is node-specific, and crashes the build. + // when { cwd } option is supported, we could change this to + // a fixture folder + t.plan(2) + t.deepEqual(entryMap('.'), { + from: path.resolve(__dirname, '..', 'index.js'), + url: 'index.js' + }) + t.deepEqual(entryMap('.:bundle.js'), { + from: path.resolve(__dirname, '..', 'index.js'), + url: 'bundle.js' + }) +}) + test('no arguments needed', function (t) { t.plan(4) var b = budo() From d871dd5c503f27deaa39eab5952a49cd76aa8d2a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 24 Sep 2015 11:40:34 -0400 Subject: [PATCH 187/302] better docs; adding current dir resolving --- CHANGELOG.md | 1 + docs/command-line-usage.md | 22 +++++++++------------- package.json | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68a223f..d5bd685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ##### Major Changes +- you can just type `budo . | garnish` for the entry point (or `index.js`) - if no `--host` is specified, resolves to internal IP - you can still hit `localhost:9966` and it will work - the ` - - \ No newline at end of file diff --git a/example/other.js b/example/other.js deleted file mode 100644 index 4f3746e..0000000 --- a/example/other.js +++ /dev/null @@ -1 +0,0 @@ -console.log('fas') diff --git a/example/simple.js b/example/simple.js deleted file mode 100644 index b206f42..0000000 --- a/example/simple.js +++ /dev/null @@ -1,4 +0,0 @@ -// npm run es6 - -import { parse } from 'url' -console.log(parse(window.location.href)) diff --git a/example/style.less b/example/style.less deleted file mode 100644 index c489d30..0000000 --- a/example/style.less +++ /dev/null @@ -1,3 +0,0 @@ -body { - background: red; -} \ No newline at end of file diff --git a/package.json b/package.json index 28dcbfd..60c7747 100644 --- a/package.json +++ b/package.json @@ -59,8 +59,6 @@ }, "scripts": { "test": "standard && tape test/test*.js | tap-spec", - "budo-less": "node example/budo-less | garnish", - "es6": "./bin/cmd.js --dir example --live example/simple.js:bundle.js -- -t babelify", "start": "./bin/cmd.js example/app.js:bundle.js --dir example | garnish", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live | garnish" }, From 897494ce9c2e6d448c62ea3413b4215bb02faa1b Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 25 Sep 2015 10:02:59 -0400 Subject: [PATCH 190/302] clean up example, use es6 --- example/app.js | 45 ++++++++++++++++++++++----------------------- example/main.css | 2 +- package.json | 11 +++-------- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/example/app.js b/example/app.js index 20386f8..652aee7 100644 --- a/example/app.js +++ b/example/app.js @@ -1,32 +1,31 @@ /*globals Image*/ -var dpr = window.devicePixelRatio || 1 -var ctx = require('2d-context')() -var fit = require('canvas-fit')(ctx.canvas, window, dpr) +import createLoop from 'canvas-loop' +import createContext from '2d-context' -// setup canvas DOM state -window.addEventListener('resize', fit, false) -require('domready')(function () { - fit() - document.body.appendChild(ctx.canvas) +const context = createContext() +const canvas = context.canvas + +const app = createLoop(canvas, { + scale: window.devicePixelRatio }) +document.body.appendChild(canvas) -var img = new Image() +const img = new Image() +img.onload = () => app.start() img.src = 'baboon.png' -var time = 0 -require('raf-loop')(function (dt) { - var width = ctx.canvas.width - var height = ctx.canvas.height - ctx.clearRect(0, 0, width, height) +let time = 0 + +app.on('tick', (dt) => { + const [ width, height ] = app.shape + context.clearRect(0, 0, width, height) time += dt / 1000 - ctx.save() - ctx.scale(dpr, dpr) - ctx.fillRect(Math.sin(time) * 50 + 300, 50, 20, 40) - ctx.fillText('from browserify!', 40, 40) - if (img.width > 0 || img.height > 0) { - ctx.drawImage(img, 50, 50) - } - ctx.restore() -}).start() + context.save() + context.scale(app.scale, app.scale) + context.fillRect(Math.sin(time) * 50 + 300, 50, 20, 40) + context.fillText('from browserify!', 40, 40) + context.drawImage(img, 50, 50) + context.restore() +}) diff --git a/example/main.css b/example/main.css index c489d30..693b449 100644 --- a/example/main.css +++ b/example/main.css @@ -1,3 +1,3 @@ body { - background: red; + background: #e1e1e1; } \ No newline at end of file diff --git a/package.json b/package.json index 60c7747..d8251f8 100644 --- a/package.json +++ b/package.json @@ -39,28 +39,23 @@ "devDependencies": { "2d-context": "^1.2.0", "babelify": "^6.3.0", + "canvas-loop": "^1.0.4", "brfs": "^1.4.0", "browserify": "^9.0.8", - "canvas-fit": "^1.2.0", - "domready": "^1.0.7", "garnish": "^3.2.1", - "inject-lr-script": "^1.0.1", - "less-css-stream": "^1.0.0", "ndjson": "^1.4.1", - "raf-loop": "^1.0.1", "request": "^2.53.0", "standard": "^5.2.1", "tap-spec": "^3.0.0", "tape": "^4.0.0", "through2": "^2.0.0", "tree-kill": "^0.1.1", - "uglify-js": "^2.4.19", "win-spawn": "^2.0.0" }, "scripts": { "test": "standard && tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/app.js:bundle.js --dir example | garnish", - "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live | garnish" + "start": "./bin/cmd.js example/app.js:bundle.js --dir example -- -t babelify | garnish", + "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -- -t babelify | garnish" }, "keywords": [ "browserify", From 0fb7495ea5115cd82a5bfc3d12e77bee1714f967 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 25 Sep 2015 10:03:50 -0400 Subject: [PATCH 191/302] 5.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8251f8..34c8892 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0-beta4", + "version": "5.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From ce980b5eabcd0666e8f9b36e7f5bfcbf950f00f1 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 25 Sep 2015 10:45:12 -0400 Subject: [PATCH 192/302] update docs --- docs/command-line-usage.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index c7b3536..3825a67 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -26,6 +26,8 @@ If you specify the current directory, it will resolve to the `"main"` field in y budo . | garnish ``` +You can see the full list of command-line flags in the [README.md](../README.md#cli). + ## index.html Notice we haven't had to write any HTML! If you want to, though, you can drop `index.html` in the same folder that you are serving budō from (or the base `--dir` folder), and it will use that instead of a dynamically generated index. @@ -104,10 +106,8 @@ budo test/*.js --serve static/bundle.js | garnish Everything after the `--` argument will not be parsed/manipulated, and will be passed directly to browserify. -Currently, this is needed when using sub-arg syntax: - ```sh -budo main.js --live -- -t [ foo --bar=555 --debug ] +budo main.js --live -- -t babelify -t glslify ``` ## launch @@ -138,4 +138,6 @@ The flag is only available in the command-line. By default, budo's server will listen on your internal IP. This address is the first message logged to terminal. -This makes it easy to test during development across devices. \ No newline at end of file +This makes it easy to test during development across devices. + +You can specify another address with the `--host` flag. \ No newline at end of file From 7e020de9e1a375fdb860d8ac46196b20c5725a1f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 25 Sep 2015 13:55:01 -0400 Subject: [PATCH 193/302] improve version display for better debugging and trouble shooting --- index.js | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 708ed22..9acd4f9 100644 --- a/index.js +++ b/index.js @@ -21,7 +21,9 @@ function budoCLI (args, opts) { delete argv['--'] if (argv.version) { - console.log(require('./package.json').version) + console.log('budo v' + require('./package.json').version) + console.log('browserify v' + require('browserify/package.json').version) + console.log('watchify v' + require('watchify-middleware').getWatchifyVersion()) return null } diff --git a/package.json b/package.json index 34c8892..2643c1b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "strip-ansi": "^3.0.0", "term-color": "^1.0.1", "tiny-lr": "^0.1.5", - "watchify-middleware": "^1.1.0", + "watchify-middleware": "^1.3.0", "xtend": "^4.0.0" }, "devDependencies": { From a8bffc9897008b614cf240af04ab2f825518689a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 25 Sep 2015 13:55:06 -0400 Subject: [PATCH 194/302] 5.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2643c1b..adc46ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.0.0", + "version": "5.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From c3f2f257314f23fb1d873ce5e0acceeca1908c88 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 2 Oct 2015 00:56:34 -0400 Subject: [PATCH 195/302] more robust port finding; fix liveReload port finding issue and CLI arg --- example/demo/index.html | 11 ++++++++ index.js | 7 +++++ lib/budo.js | 42 +++++++++++++++++++---------- lib/get-ports.js | 59 ++++++++++++++++++++++++++++++++--------- lib/parse-args.js | 2 +- lib/tinylr.js | 3 +-- package.json | 2 +- 7 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 example/demo/index.html diff --git a/example/demo/index.html b/example/demo/index.html new file mode 100644 index 0000000..1659b34 --- /dev/null +++ b/example/demo/index.html @@ -0,0 +1,11 @@ + + + + + + budo + + + + + \ No newline at end of file diff --git a/index.js b/index.js index 9acd4f9..eb22373 100644 --- a/index.js +++ b/index.js @@ -38,6 +38,13 @@ function budoCLI (args, opts) { console.error(color.yellow('WARNING'), '--outfile has been removed in budo@3.0') } + if (typeof argv.port === 'string') { + argv.port = parseInt(argv.port, 10) + } + if (typeof argv.livePort === 'string') { + argv.livePort = parseInt(argv.livePort, 10) + } + // opts.live can be a glob or a boolean if (typeof argv.live === 'string' && /(true|false)/.test(argv.live)) { argv.live = argv.live === 'true' diff --git a/lib/budo.js b/lib/budo.js index b1438fe..425bfb4 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -95,6 +95,10 @@ function createBudo (entries, opts) { var deferredWatch = noop var deferredLive = noop + // keep track of the original host + // (can be undefined) + var hostAddress = opts.host + // public API emitter.close = once(close) emitter.reload = reload @@ -161,18 +165,33 @@ function createBudo (entries, opts) { // destroy previous if (tinylr) tinylr.close() - liveOpts = xtend({ - host: opts.host, - port: opts.livePort - }, liveOpts) + // default port + liveOpts = xtend({ port: opts.livePort }, liveOpts) + + // default to budo host + var hostName = liveOpts.host ? getHostAddress(liveOpts.host) : opts.host + + // the LiveReload ') + app.close() + }) + }) + .on('exit', function () { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) +}) + test('favicon.ico should have status code 200', function (t) { t.plan(2) var app = budo(entry, { dir: __dirname }) From bc69f9f04d38d137e2eed316152fcdeb6cee0754 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 29 Oct 2015 11:53:25 -0400 Subject: [PATCH 213/302] 6.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83d5260..0accca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "5.1.5", + "version": "6.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 5e65b246cb493db53aab95baf51acf5f9b0d07c8 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 29 Oct 2015 12:04:59 -0400 Subject: [PATCH 214/302] clean up package.json; doc new features --- docs/api-usage.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api-usage.md b/docs/api-usage.md index 745b241..1106718 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -55,6 +55,10 @@ All settings are optional. - whether to enable source maps from browserify (default `true`) - `serve` (String) - if specified, the `' +var defaultIndex = 'budo' +var scriptIndex = 'budo' test('accept "." entry point', function (t) { // currently we can't test budo('.') because the index.js @@ -34,7 +34,7 @@ test('no arguments needed', function (t) { request.get({ url: ev.uri }, function (err, resp, body) { if (err) return t.fail(err) t.equal(resp.statusCode, 200, 'still gets generated index.html') - t.equal(body, defaultIndex) + t.equal(body, defaultIndex, 'should match default index') b.close() }) }) diff --git a/test/test-live.js b/test/test-live.js index 25b3a8a..2432db4 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -108,9 +108,9 @@ function matchesHTML (t, uri, html, cb) { } function getHTMLNoLive () { - return '' + return 'budo' } function getHTML () { - return '' + return 'budo' } From c2f966f468f5fba0938d4f716f6e7831b243a05d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 9 Nov 2015 12:30:31 -0500 Subject: [PATCH 221/302] 6.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac90168..2400bbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.0.1", + "version": "6.0.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 7db0be86fc6ebd65f38f39f0f44d62ea11b528ec Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 11:37:44 -0500 Subject: [PATCH 222/302] fix entry mapping for windows and letter drive absolute paths --- lib/map-entry.js | 9 ++++++++- test/test-map-entry.js | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/test-map-entry.js diff --git a/lib/map-entry.js b/lib/map-entry.js index 3853eb8..f600032 100644 --- a/lib/map-entry.js +++ b/lib/map-entry.js @@ -8,7 +8,14 @@ function mapEntry (file) { file = entry() } - var parts = file.split(':') + var parts + // absolute path with letter drive, eg C:/ + if (/^[A-Z]\:\/+/.test(file)) { + parts = file.split(/\:(?:(?=[^\/]))/) + } else { + parts = file.split(':') + } + var pathFrom, pathUrl if (parts.length > 1 && parts[1].length > 0) { diff --git a/test/test-map-entry.js b/test/test-map-entry.js new file mode 100644 index 0000000..59ac302 --- /dev/null +++ b/test/test-map-entry.js @@ -0,0 +1,14 @@ +var test = require('tape') +var mapEntry = require('../lib/map-entry') + +test('should map entry paths', function (t) { + t.deepEqual(mapEntry('foo.js'), { from: 'foo.js', url: 'foo.js' }) + t.deepEqual(mapEntry('foo/bar.js'), { from: 'foo/bar.js', url: 'bar.js' }) + t.deepEqual(mapEntry('foo/bar.js:bundle.js'), { from: 'foo/bar.js', url: 'bundle.js' }) + t.deepEqual(mapEntry('foo/bar.js:bundle.js?foo'), { from: 'foo/bar.js', url: 'bundle.js?foo' }) + t.deepEqual(mapEntry('/absolute/path.js:bundle.js?foo'), { from: '/absolute/path.js', url: 'bundle.js?foo' }) + t.deepEqual(mapEntry('/absolute/path.js'), { from: '/absolute/path.js', url: 'path.js' }) + t.deepEqual(mapEntry('C:/absolute/path.js'), { from: 'C:/absolute/path.js', url: 'path.js' }) + t.deepEqual(mapEntry('C:/absolute/path.js:bundle.js'), { from: 'C:/absolute/path.js', url: 'bundle.js' }) + t.end() +}) From fbcef499d7d4bf798a6566842bee37888f30b34f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 11:37:51 -0500 Subject: [PATCH 223/302] 6.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2400bbf..0df51c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.0.2", + "version": "6.0.3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From e02eb2f69e7d4b957823016effe6e0c12fa288df Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 13:54:08 -0500 Subject: [PATCH 224/302] update the test to support back slashes --- lib/map-entry.js | 4 ++-- test/test-map-entry.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/map-entry.js b/lib/map-entry.js index f600032..da4e5b9 100644 --- a/lib/map-entry.js +++ b/lib/map-entry.js @@ -10,8 +10,8 @@ function mapEntry (file) { var parts // absolute path with letter drive, eg C:/ - if (/^[A-Z]\:\/+/.test(file)) { - parts = file.split(/\:(?:(?=[^\/]))/) + if (/^[A-Z]\:[\/\\]+/.test(file)) { + parts = file.split(/\:(?:(?=[^\/\\]))/) } else { parts = file.split(':') } diff --git a/test/test-map-entry.js b/test/test-map-entry.js index 59ac302..42ed006 100644 --- a/test/test-map-entry.js +++ b/test/test-map-entry.js @@ -6,9 +6,12 @@ test('should map entry paths', function (t) { t.deepEqual(mapEntry('foo/bar.js'), { from: 'foo/bar.js', url: 'bar.js' }) t.deepEqual(mapEntry('foo/bar.js:bundle.js'), { from: 'foo/bar.js', url: 'bundle.js' }) t.deepEqual(mapEntry('foo/bar.js:bundle.js?foo'), { from: 'foo/bar.js', url: 'bundle.js?foo' }) + t.deepEqual(mapEntry('f\\bar.js:bundle.js?foo'), { from: 'f\\bar.js', url: 'bundle.js?foo' }) t.deepEqual(mapEntry('/absolute/path.js:bundle.js?foo'), { from: '/absolute/path.js', url: 'bundle.js?foo' }) t.deepEqual(mapEntry('/absolute/path.js'), { from: '/absolute/path.js', url: 'path.js' }) t.deepEqual(mapEntry('C:/absolute/path.js'), { from: 'C:/absolute/path.js', url: 'path.js' }) t.deepEqual(mapEntry('C:/absolute/path.js:bundle.js'), { from: 'C:/absolute/path.js', url: 'bundle.js' }) + t.deepEqual(mapEntry('C://absolute//path.js:bundle.js'), { from: 'C://absolute//path.js', url: 'bundle.js' }) + t.deepEqual(mapEntry('C:\\absolute\\path.js:bundle.js'), { from: 'C:\\absolute\\path.js', url: 'bundle.js' }) t.end() }) From 953afac2b7c0d4bb254fec3a31cad907eb508ded Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 13:54:13 -0500 Subject: [PATCH 225/302] 6.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0df51c3..9958cfa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.0.3", + "version": "6.0.4", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From ccdc017ebeac4e89f8cc4470512972334e3569a1 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 13:55:30 -0500 Subject: [PATCH 226/302] one more test to map entry --- test/test-map-entry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-map-entry.js b/test/test-map-entry.js index 42ed006..e7d8e44 100644 --- a/test/test-map-entry.js +++ b/test/test-map-entry.js @@ -4,6 +4,7 @@ var mapEntry = require('../lib/map-entry') test('should map entry paths', function (t) { t.deepEqual(mapEntry('foo.js'), { from: 'foo.js', url: 'foo.js' }) t.deepEqual(mapEntry('foo/bar.js'), { from: 'foo/bar.js', url: 'bar.js' }) + t.deepEqual(mapEntry('./foo/bar.js'), { from: './foo/bar.js', url: 'bar.js' }) t.deepEqual(mapEntry('foo/bar.js:bundle.js'), { from: 'foo/bar.js', url: 'bundle.js' }) t.deepEqual(mapEntry('foo/bar.js:bundle.js?foo'), { from: 'foo/bar.js', url: 'bundle.js?foo' }) t.deepEqual(mapEntry('f\\bar.js:bundle.js?foo'), { from: 'f\\bar.js', url: 'bundle.js?foo' }) From 3b275e64f26d04ad722255377091c6f05f14bb2d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 10 Nov 2015 14:01:24 -0500 Subject: [PATCH 227/302] add a failing test; will have to investigate --- test/test-map-entry.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test-map-entry.js b/test/test-map-entry.js index e7d8e44..44e4d4b 100644 --- a/test/test-map-entry.js +++ b/test/test-map-entry.js @@ -14,5 +14,8 @@ test('should map entry paths', function (t) { t.deepEqual(mapEntry('C:/absolute/path.js:bundle.js'), { from: 'C:/absolute/path.js', url: 'bundle.js' }) t.deepEqual(mapEntry('C://absolute//path.js:bundle.js'), { from: 'C://absolute//path.js', url: 'bundle.js' }) t.deepEqual(mapEntry('C:\\absolute\\path.js:bundle.js'), { from: 'C:\\absolute\\path.js', url: 'bundle.js' }) + + // This one is failing. Maybe because of OS I am running tests on?? + // t.deepEqual(mapEntry('C:\\absolute\\path.js'), { from: 'C:\\absolute\\path.js', url: 'path.js' }) t.end() }) From d7feb08ce9592a9c0ab1ee787f1268d56383878c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 14 Nov 2015 18:14:17 -0500 Subject: [PATCH 228/302] imrpoving garnish logs --- lib/budo.js | 15 +++++++++++---- lib/parse-args.js | 4 +++- lib/server.js | 26 +++++++++++++++++++++----- lib/tinylr.js | 2 -- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index a11d543..d2d2bce 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -38,7 +38,10 @@ function createBudo (entries, opts) { if (opts.stream) { // by default, pretty-print to the stream with info logging if (!opts.ndjson) { - var pretty = garnish({ level: 'info' }) + var pretty = garnish({ + level: opts.verbose ? 'debug' : 'info', + name: 'budo' + }) pretty.pipe(opts.stream) opts.stream = pretty } @@ -78,10 +81,14 @@ function createBudo (entries, opts) { bundler.on('log', function (ev) { if (ev.type === 'bundle') { ev.url = '/' + opts.serve - ev.name = 'browserify' var time = ev.elapsed - ev.elapsed = time + 'ms' - ev.colors = { elapsed: time > 1000 ? 'yellow' : 'dim' } + ev.elapsed = time + ev.name = 'browserify' + ev.type = undefined + ev.colors = { + elapsed: time > 1000 ? 'yellow' : 'dim', + message: 'dim ' + } } log.info(ev) }) diff --git a/lib/parse-args.js b/lib/parse-args.js index a3d58a0..814d3da 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -11,7 +11,8 @@ function parseArgs (args, opt) { 'live', 'open', 'portfind', - 'ndjson' + 'ndjson', + 'verbose' ], string: [ 'host', @@ -24,6 +25,7 @@ function parseArgs (args, opt) { default: module.exports.defaults, alias: { port: 'p', + verbose: 'v', help: 'h', host: 'H', dir: 'd', diff --git a/lib/server.js b/lib/server.js index f206d86..add27f1 100644 --- a/lib/server.js +++ b/lib/server.js @@ -72,24 +72,40 @@ function createServer (entryHandler, opts) { return server function logger (type, req, res) { + var byteLength = 0 var now = Date.now() var fn = function () { var elapsed = Date.now() - now log.info({ - elapsed: elapsed + 'ms', - name: 'http', - message: (req.method || 'GET').toUpperCase(), + elapsed: elapsed, + contentLength: byteLength, + method: (req.method || 'GET').toUpperCase(), url: req.url, statusCode: res.statusCode, type: type, colors: { - elapsed: elapsed > 1000 ? 'yellow' : 'dim', - message: 'dim' + elapsed: elapsed > 1000 ? 'yellow' : 'dim' } }) } res.once('finish', fn) + var end = res.end + var write = res.write + + // catch content-length of payload + res.write = function (payload) { + if (payload) byteLength += Buffer.byteLength(payload.toString(), 'utf8') + res.write = write + res.write.apply(res, arguments) + } + + res.end = function (payload) { + if (payload) byteLength += Buffer.byteLength(payload.toString(), 'utf8') + res.end = end + res.end.apply(res, arguments) + } + // allow removal return function () { res.removeListener('finish', fn) diff --git a/lib/tinylr.js b/lib/tinylr.js index 51a57cb..b3b099e 100644 --- a/lib/tinylr.js +++ b/lib/tinylr.js @@ -40,8 +40,6 @@ module.exports = function (opt) { close: close, reload: function reload (path) { - // this starts to clutter things a bit ... - // log.info({ name: 'live', url: path, type: 'reload' }) try { server.changed({ body: { From 6073e301f475e8aa14fe6f9ca23a089fbe85cf22 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 16 Nov 2015 10:34:36 -0500 Subject: [PATCH 229/302] hide URL in bundle --- lib/budo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/budo.js b/lib/budo.js index d2d2bce..99741b5 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -80,7 +80,7 @@ function createBudo (entries, opts) { bundler.on('log', function (ev) { if (ev.type === 'bundle') { - ev.url = '/' + opts.serve + // ev.url = '/' + opts.serve var time = ev.elapsed ev.elapsed = time ev.name = 'browserify' From 906f5b22b2c6774be7f8ede618bba85ec19844dc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 16 Nov 2015 10:50:38 -0500 Subject: [PATCH 230/302] add debug for how many files change (experiment); remove file-watch debug --- lib/budo.js | 11 ++++++++++- lib/file-watch.js | 5 ----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 99741b5..67bcd2e 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -89,8 +89,8 @@ function createBudo (entries, opts) { elapsed: time > 1000 ? 'yellow' : 'dim', message: 'dim ' } + log.info(ev) } - log.info(ev) }) // uncaught syntax errors should not stop the server @@ -101,6 +101,15 @@ function createBudo (entries, opts) { bundler.on('bundle-error', emitter.emit.bind(emitter, 'bundle-error')) bundler.on('update', emitter.emit.bind(emitter, 'update')) bundler.on('pending', emitter.emit.bind(emitter, 'pending')) + + emitter.on('update', function (contents, deps) { + if (deps.length > 1) { + log.debug({ + name: 'browserify', + message: deps.length + ' files changed' + }) + } + }) } var server = createServer(middleware, opts) diff --git a/lib/file-watch.js b/lib/file-watch.js index 0c02322..41c2cc9 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -33,11 +33,6 @@ module.exports = function (glob, opt) { function onWatch (event, path) { emitter.emit('watch', event, path) - log.debug({ - name: 'file', - type: event, - url: path - }) } emitter.close = function () { From 603080b95c0842a6d2186df12adfd6498adfe9b7 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Nov 2015 10:26:44 -0500 Subject: [PATCH 231/302] better docs --- docs/command-line-usage.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index b4851b4..7f7815f 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -97,10 +97,10 @@ budo index.js --live=*.{html,css} Budo also supports multiple entry points; they will all get concatenated into a single bundle. If you aren't using a colon separator (`:`), the entry point will default to the first path. Or, you can explicitly set the path with the `--serve` option, as below: ```sh -budo test/*.js --serve static/bundle.js +budo test/one.js test/two.js --serve static/bundle.js ``` -*Note:* This uses unix glob expansion and may not work on Windows. +Note the path here is relative to your `--dir` folder from where the `index.html` is being served. ## browserify arguments @@ -110,6 +110,8 @@ Everything after the `--` argument will not be parsed/manipulated, and will be p budo main.js --live -- -t babelify -t glslify ``` +You can also add a [`"browserify"` field](https://github.com/substack/browserify-handbook#browserifytransform-field) to your `package.json` file, and budo will use that config. This is not typically recommended for modules, but it can be useful when building applications. + ## launch To launch the browser once the server connects, you can use the `--open` or `-o` flag: From 48944f7e2b6e6f5016a762d739e290139972fc33 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Nov 2015 10:49:21 -0500 Subject: [PATCH 232/302] search each static folder for html index --- lib/server.js | 10 +++++++--- package.json | 1 + test/fixtures/three/index.html | 1 + test/test-static-folders.js | 21 +++++++++++++++++---- 4 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/three/index.html diff --git a/lib/server.js b/lib/server.js index f206d86..ed216f5 100644 --- a/lib/server.js +++ b/lib/server.js @@ -7,6 +7,7 @@ var inject = require('inject-lr-script') var urlParse = require('url').parse var fs = require('fs') var log = require('bole')('budo') +var asyncDetectSeries = require('async').detectSeries module.exports = createServer function createServer (entryHandler, opts) { @@ -148,16 +149,19 @@ function createServer (entryHandler, opts) { } function home (req, res) { - fs.exists(path.join(basedir, 'index.html'), function (exists) { + var homeFiles = staticPaths.map(function (base) { + return path.join(base, 'index.html') + }) + asyncDetectSeries(homeFiles, fs.exists, function (result) { // inject LiveReload into HTML content if needed if (live && !live.plugin) { res = inject(res, live) } - var type = exists ? 'static' : 'generated' + var type = result ? 'static' : 'generated' logger(type, req, res) - if (exists) { + if (result) { staticRequest(req, res) } else { generateIndex(req, res) diff --git a/package.json b/package.json index 9958cfa..6d0e5f1 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "url": "https://github.com/mattdesl" }, "dependencies": { + "async": "^1.5.0", "bole": "^2.0.0", "browserify": "^12.0.1", "chokidar": "^1.0.1", diff --git a/test/fixtures/three/index.html b/test/fixtures/three/index.html new file mode 100644 index 0000000..1d144f6 --- /dev/null +++ b/test/fixtures/three/index.html @@ -0,0 +1 @@ +THREE \ No newline at end of file diff --git a/test/test-static-folders.js b/test/test-static-folders.js index 3b8acd9..128c163 100644 --- a/test/test-static-folders.js +++ b/test/test-static-folders.js @@ -5,6 +5,7 @@ var request = require('request') var path1 = path.resolve(__dirname, 'fixtures', 'one') var path2 = path.resolve(__dirname, 'fixtures', 'two') +var path3 = path.resolve(__dirname, 'fixtures', 'three') test('should serve multiple folders', function (t) { t.plan(2) @@ -24,10 +25,8 @@ test('should serve multiple folders', function (t) { }) }) -// currently failing -/* test('should find any index.html', function (t) { - t.plan(2) + t.plan(1) var expected = 'TWO' var app = budo({ @@ -40,4 +39,18 @@ test('should find any index.html', function (t) { }) }) }) -*/ + +test('should find the first index.html', function (t) { + t.plan(1) + + var expected = 'THREE' + var app = budo({ + dir: [ path1, path3, path2 ] + }).on('connect', function (ev) { + request.get({ uri: ev.uri + 'index.html' }, function (err, res, body) { + if (err) return t.fail(err) + t.equal(body, expected, 'gets the first index.html in all static folders') + app.close() + }) + }) +}) From c1ec9ffb3f00b85be80d7e93b74c238f2f39e333 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Nov 2015 10:49:53 -0500 Subject: [PATCH 233/302] remove unused line --- lib/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index ed216f5..3cb8857 100644 --- a/lib/server.js +++ b/lib/server.js @@ -16,7 +16,6 @@ function createServer (entryHandler, opts) { if (staticPaths.length === 0) { staticPaths = [ process.cwd() ] } - var basedir = staticPaths[0] var staticHandlers = staticPaths.map(function (filepath, i, list) { var last = i === list.length - 1 From 70f1c15dd90c9a62ee1da06d805f607def23010e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 24 Nov 2015 10:50:04 -0500 Subject: [PATCH 234/302] 6.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d0e5f1..eebae47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.0.4", + "version": "6.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 81c9e3365b487540fa84276b2250250056c95ac6 Mon Sep 17 00:00:00 2001 From: Christopher Van Date: Fri, 25 Sep 2015 16:17:47 -0700 Subject: [PATCH 235/302] enable livereload on directories, not strictly files ending with `.html` (fixes #72) --- lib/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index 3cb8857..caef015 100644 --- a/lib/server.js +++ b/lib/server.js @@ -63,7 +63,7 @@ function createServer (entryHandler, opts) { router.addRoute('/index.html', home) router.addRoute('/', home) router.addRoute('/favicon.ico', favicon) - router.addRoute('*.html', wildcard(true)) + router.addRoute('*.html?|*[^.]', wildcard(true)) router.addRoute('*', wildcard()) // allow user to toggle live reload integration From 5de75fad5930b3afaa9112517c12d06898634d4a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 10:12:10 -0500 Subject: [PATCH 236/302] 6.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eebae47..6dcb4d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.1.0", + "version": "6.1.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From bbeba86d1987735df0b42d435451206eb79e4ba0 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:10:42 -0500 Subject: [PATCH 237/302] fix live-reload for directory path which doesnt end in HTML --- example/demo/index.html | 1 - lib/server.js | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/demo/index.html b/example/demo/index.html index 1659b34..8daa6f6 100644 --- a/example/demo/index.html +++ b/example/demo/index.html @@ -6,6 +6,5 @@ budo - \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index ca5ad5d..c68c282 100644 --- a/lib/server.js +++ b/lib/server.js @@ -63,7 +63,10 @@ function createServer (entryHandler, opts) { router.addRoute('/index.html', home) router.addRoute('/', home) router.addRoute('/favicon.ico', favicon) - router.addRoute('*.html?|*[^.]', wildcard(true)) + router.addRoute('*.html?', wildcard(true)) + if (!opts.pushstate) { + router.addRoute('/**/', wildcard(true)) + } router.addRoute('*', wildcard()) // allow user to toggle live reload integration From 2118ea523f63238425ffeafa4f02efaad877bc53 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:28:56 -0500 Subject: [PATCH 238/302] hide static since its assumed --- lib/server.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index c68c282..a66d446 100644 --- a/lib/server.js +++ b/lib/server.js @@ -85,7 +85,7 @@ function createServer (entryHandler, opts) { method: (req.method || 'GET').toUpperCase(), url: req.url, statusCode: res.statusCode, - type: type, + type: type === 'static' ? undefined : type, colors: { elapsed: elapsed > 1000 ? 'yellow' : 'dim' } diff --git a/package.json b/package.json index 6dcb4d9..0c677ff 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "chokidar": "^1.0.1", "ecstatic": "^1.0.0", "events": "^1.0.2", - "garnish": "^4.0.0", + "garnish": "^4.1.1", "get-ports": "^1.0.2", "inject-lr-script": "^1.0.0", "internal-ip": "^1.0.1", From 3444473594f1d58761b3beed5fd411fd5e2aa930 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:34:45 -0500 Subject: [PATCH 239/302] update to latest garnish and update docs --- README.md | 3 ++- docs/command-line-usage.md | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a361ea7..554687f 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ Then open [http://localhost:9966/](http://localhost:9966/) to see the content in By default, budo pretty-prints to terminal with [garnish](https://github.com/mattdesl/garnish). -
+ +
See [docs](#docs) for more details. PRs/suggestions/comments welcome. diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index 7f7815f..b96f44d 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -16,7 +16,7 @@ budo index.js Open [http://localhost:9966/](http://localhost:9966/) to see the bundled result of `index.js`. -
+
Saving `index.js` will be incremental, which means it will be fast even if your app spans hundreds of modules. diff --git a/package.json b/package.json index 0c677ff..f854a37 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "chokidar": "^1.0.1", "ecstatic": "^1.0.0", "events": "^1.0.2", - "garnish": "^4.1.1", + "garnish": "^5.0.0", "get-ports": "^1.0.2", "inject-lr-script": "^1.0.0", "internal-ip": "^1.0.1", From fb365b3ff6ff3ec9be4f12edd724831cd0b1b740 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:35:02 -0500 Subject: [PATCH 240/302] 6.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f854a37..944cc05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.1.1", + "version": "6.2.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 460a8e423e0411c8b1cb0a32c42ea92b4e6fb855 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:45:12 -0500 Subject: [PATCH 241/302] remove unused log --- lib/file-watch.js | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/file-watch.js b/lib/file-watch.js index 41c2cc9..ee5a8f2 100644 --- a/lib/file-watch.js +++ b/lib/file-watch.js @@ -1,5 +1,4 @@ // a thin wrapper around chokidar file watching HTML / CSS -var log = require('bole')('budo') var watch = require('chokidar').watch var xtend = require('xtend') var Emitter = require('events/') diff --git a/package.json b/package.json index 944cc05..6d361aa 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "win-spawn": "^2.0.0" }, "scripts": { - "test": "standard && tape test/test*.js | tap-spec", + "test": "standard && tape test/test*.js", "start": "./bin/cmd.js example/app.js:bundle.js --dir example -- -t babelify", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -- -t babelify" }, From 6a251ae4196c8b949808fa4e62785a6da5d0490e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:45:22 -0500 Subject: [PATCH 242/302] put tap spec back --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d361aa..944cc05 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "win-spawn": "^2.0.0" }, "scripts": { - "test": "standard && tape test/test*.js", + "test": "standard && tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js:bundle.js --dir example -- -t babelify", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -- -t babelify" }, From cf8f1612393b6877ee2608396a6f7303eb5248bc Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:50:41 -0500 Subject: [PATCH 243/302] fix ndjson parsing with new garnish logs --- test/test-entries.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-entries.js b/test/test-entries.js index 6bd8ed2..081bf2a 100644 --- a/test/test-entries.js +++ b/test/test-entries.js @@ -61,8 +61,8 @@ test('user can build their own server', function (t) { t.plan(5) var stream = ndjson.parse() stream.on('data', function (data) { - if (data.name === 'http' && data.url === '/foo.js') { - t.equal(data.type, 'middleware') + if (data.url === '/foo.js') { + t.equal(data.type, 'middleware', 'got middleware') } }) From b70a5d4173551321dbf5387133ece32d129e5b18 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:58:14 -0500 Subject: [PATCH 244/302] clean up code and add docs to changelog and command line usage; fix tag and prepare for v7 --- CHANGELOG.md | 16 ++++++++++++++++ README.md | 1 + bin/help.txt | 1 + docs/api-usage.md | 2 ++ lib/budo.js | 1 - package.json | 6 +++--- 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec66d5..5df50d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# 7.0.0 + +Fixes previous patch and also updates to garnish redesign, leading to new log styles. + +Since various ndjson flags have changed, this is a potentially breaking change. + +Also added a `--verbose` / `-v` option like watchify, which adds some additional debug messages. + +# 6.1.1 + +Fixes live reload for directory routes like `localhost:9966/mydir`. + +# 6.1.0 + +Search for `index.html` across all static `--dir` folders, finding the first one. + # 6.0.0 ##### Major Changes diff --git a/README.md b/README.md index 554687f..12ab100 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ Options: --title optional title for default index.html --css optional stylesheet href for default index.html --ndjson print ndjson instead of pretty-printed logs + --verbose, -v also include debug messages --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding diff --git a/bin/help.txt b/bin/help.txt index 60e300a..2bc38b1 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -17,6 +17,7 @@ Options: --title optional title for default index.html --css optional stylesheet href for default index.html --ndjson print ndjson instead of pretty-printed logs + --verbose, -v also include debug messages --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding diff --git a/docs/api-usage.md b/docs/api-usage.md index 7f4e153..c20dfbc 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -71,6 +71,8 @@ All settings are optional. - can be a `reporter(err)` function which takes an Error and returns the new bundle contents - `pushstate` (Boolean) - enable push state support, which defaults 404 routes to the index (default `false`) +- `verbose` (Boolean) + - also print `'debug'` level messages to garnish; such as the pending state of the bundle and how many files changed in the last update. - `defaultIndex` (Function) - a function `fn(params)` that returns a Readable stream, takes the following `params`: `{ entry: opts.serve, title: opts.title, css: opts.css }` diff --git a/lib/budo.js b/lib/budo.js index 67bcd2e..e240643 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -80,7 +80,6 @@ function createBudo (entries, opts) { bundler.on('log', function (ev) { if (ev.type === 'bundle') { - // ev.url = '/' + opts.serve var time = ev.elapsed ev.elapsed = time ev.name = 'browserify' diff --git a/package.json b/package.json index 944cc05..92ee11c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.2.0", + "version": "6.1.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { @@ -53,8 +53,8 @@ }, "scripts": { "test": "standard && tape test/test*.js | tap-spec", - "start": "./bin/cmd.js example/app.js:bundle.js --dir example -- -t babelify", - "live": "./bin/cmd.js example/app.js:bundle.js --dir example --live -- -t babelify" + "start": "./bin/cmd.js example/app.js:bundle.js -v --dir example -- -t babelify", + "live": "./bin/cmd.js example/app.js:bundle.js --dir example --verbose --live -- -t babelify" }, "keywords": [ "browserify", From d31ec81e1e47835d0a27a8eecf32752ff506c503 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 11:58:19 -0500 Subject: [PATCH 245/302] 7.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 92ee11c..50a032b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "6.1.1", + "version": "7.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 54c022845364e2f23bd209360ffdd7034846f4f4 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 2 Dec 2015 12:56:02 -0500 Subject: [PATCH 246/302] experimental feature to force default index even when static dirs might have index.html files --- lib/server.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index a66d446..45556cc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -166,11 +166,21 @@ function createServer (entryHandler, opts) { }) } - function home (req, res) { + function findDefaultIndex (cb) { + if (opts.forceDefaultIndex) { + return process.nextTick(function () { + cb(false) + }) + } + var homeFiles = staticPaths.map(function (base) { return path.join(base, 'index.html') }) - asyncDetectSeries(homeFiles, fs.exists, function (result) { + asyncDetectSeries(homeFiles, fs.exists, cb) + } + + function home (req, res) { + findDefaultIndex(function (result) { // inject LiveReload into HTML content if needed if (live && !live.plugin) { res = inject(res, live) From f1de9f1725077561ad74cd274e99065598ae377d Mon Sep 17 00:00:00 2001 From: Christopher Van Date: Thu, 3 Dec 2015 05:20:51 -0800 Subject: [PATCH 247/302] use default ports if empty/invalid (fixes #110) --- index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/index.js b/index.js index 5004990..f0e2889 100644 --- a/index.js +++ b/index.js @@ -46,6 +46,14 @@ function budoCLI (args, opts) { argv.livePort = parseInt(argv.livePort, 10) } + // port and livePort must be valid numbers + if (isNaN(argv.port)) { + delete argv.port + } + if (isNaN(argv.livePort)) { + delete argv.livePort + } + // opts.live can be a glob or a boolean if (typeof argv.live === 'string' && /(true|false)/.test(argv.live)) { argv.live = argv.live === 'true' From c8c305569fa3b98357ef7bf45a33e4ee11c6912c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 3 Dec 2015 12:30:55 -0500 Subject: [PATCH 248/302] 7.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 50a032b..53fd52f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.0.0", + "version": "7.0.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 2d9ddb400780220d524a5fef73143cd13ea4d6fd Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 4 Dec 2015 10:08:55 -0500 Subject: [PATCH 249/302] Revert "use default ports if empty/invalid (fixes #110)" This reverts commit f1de9f1725077561ad74cd274e99065598ae377d. --- index.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/index.js b/index.js index f0e2889..5004990 100644 --- a/index.js +++ b/index.js @@ -46,14 +46,6 @@ function budoCLI (args, opts) { argv.livePort = parseInt(argv.livePort, 10) } - // port and livePort must be valid numbers - if (isNaN(argv.port)) { - delete argv.port - } - if (isNaN(argv.livePort)) { - delete argv.livePort - } - // opts.live can be a glob or a boolean if (typeof argv.live === 'string' && /(true|false)/.test(argv.live)) { argv.live = argv.live === 'true' From 7ad4013ce88504619886f8af833a857ff494442f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 4 Dec 2015 10:09:17 -0500 Subject: [PATCH 250/302] 7.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53fd52f..e035bb2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.0.1", + "version": "7.0.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 036f6b2d26f457a0f34cc458b5ab7c1e122ad1e7 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 14 Dec 2015 16:28:22 -0500 Subject: [PATCH 251/302] add docs on pushstate and gotcha with base tag --- docs/api-usage.md | 1 + docs/command-line-usage.md | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/api-usage.md b/docs/api-usage.md index c20dfbc..69a669e 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -71,6 +71,7 @@ All settings are optional. - can be a `reporter(err)` function which takes an Error and returns the new bundle contents - `pushstate` (Boolean) - enable push state support, which defaults 404 routes to the index (default `false`) + - Recommended you also add something like `` to your HTML `` - `verbose` (Boolean) - also print `'debug'` level messages to garnish; such as the pending state of the bundle and how many files changed in the last update. - `defaultIndex` (Function) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index b96f44d..3d9484d 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -142,4 +142,20 @@ By default, budo's server will listen on your internal IP. This address is the f This makes it easy to test during development across devices. -You can specify another address with the `--host` flag. \ No newline at end of file +You can specify another address with the `--host` flag. + +## pushstate + +You can set a `--pushstate` flag to make the server capable for HTML5 pushState Single-Page Applications. + +Now, any 404 requests (such as `/foo/bar/blah`) will get routed to the home `index.html`. + +It is suggested you add a `` in your `index.html` for this to work with nested paths: + +```html + + + + + +``` From c2ea5c362dd4b9399f4ed8026c39918af316235b Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 17 Dec 2015 07:56:15 -0500 Subject: [PATCH 252/302] Add failing test for double-logging issue Refs #116 --- test/test-log.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/test-log.js diff --git a/test/test-log.js b/test/test-log.js new file mode 100644 index 0000000..e5d59bb --- /dev/null +++ b/test/test-log.js @@ -0,0 +1,31 @@ +var test = require('tape') +var through = require('through2') +var budo = require('../') + +var entry = 'test/fixtures/app.js' + +test('should log properly after restarting the server', function (t) { + var out = through.obj(function (item, _, next) { + t.ok(item, 'log message') + next() + }) + + // 2 messages for each server run: 1 for server startup and 1 for bundle complete + t.plan(4) + // start a self-closing server, and then do it again (once) when it exits + start().once('exit', start) + + function start () { + var b = budo(entry, { + dir: __dirname, + stream: out, + ndjson: true + }) + + return b.once('connect', function (ev) { + b.close() + }) + .on('error', t.fail.bind(t)) + } +}) + From b603c7901afe3ac1d67aa65ddcfb2d554cb6d538 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 17 Dec 2015 08:02:16 -0500 Subject: [PATCH 253/302] Reset bole logger at startup Closes #116 --- lib/budo.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/budo.js b/lib/budo.js index e240643..4dd96f8 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -17,10 +17,11 @@ var createTinylr = require('./tinylr') var mapEntry = require('./map-entry') var noop = function () {} -var log = bole('budo') module.exports = createBudo function createBudo (entries, opts) { + var log = bole('budo') + // if no entries are specified, just options if (entries && !Array.isArray(entries) && typeof entries === 'object') { opts = entries @@ -46,6 +47,7 @@ function createBudo (entries, opts) { opts.stream = pretty } + bole.reset() bole.output({ stream: opts.stream, level: 'debug' From d7309fe19247a1997d3f2a6831a91890a9d5b7ad Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 17 Dec 2015 11:34:06 -0500 Subject: [PATCH 254/302] fix #115; logging file size of requests --- lib/server.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/server.js b/lib/server.js index 45556cc..60647f4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -93,22 +93,15 @@ function createServer (entryHandler, opts) { } res.once('finish', fn) - var end = res.end var write = res.write // catch content-length of payload res.write = function (payload) { - if (payload) byteLength += Buffer.byteLength(payload.toString(), 'utf8') + if (payload) byteLength += payload.length res.write = write res.write.apply(res, arguments) } - res.end = function (payload) { - if (payload) byteLength += Buffer.byteLength(payload.toString(), 'utf8') - res.end = end - res.end.apply(res, arguments) - } - // allow removal return function () { res.removeListener('finish', fn) From 768ee13df59d2c8be78cd14403dd01d9ff74e557 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 17 Dec 2015 11:35:35 -0500 Subject: [PATCH 255/302] 7.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e035bb2..165f44b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.0.2", + "version": "7.0.3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 92f0cf8e36edcb2e3ccf20d23bd2e8fdb32fed3d Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 17 Dec 2015 12:24:18 -0500 Subject: [PATCH 256/302] Move bole.reset() from startup to shutdown --- lib/budo.js | 2 +- test/test-log.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index 4dd96f8..2047afd 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -47,7 +47,6 @@ function createBudo (entries, opts) { opts.stream = pretty } - bole.reset() bole.output({ stream: opts.stream, level: 'debug' @@ -289,6 +288,7 @@ function createBudo (entries, opts) { process.nextTick(next) } + if (started) bole.reset() if (started) server.close() if (tinylr) tinylr.close() if (bundler) bundler.close() diff --git a/test/test-log.js b/test/test-log.js index e5d59bb..87d0851 100644 --- a/test/test-log.js +++ b/test/test-log.js @@ -6,12 +6,14 @@ var entry = 'test/fixtures/app.js' test('should log properly after restarting the server', function (t) { var out = through.obj(function (item, _, next) { - t.ok(item, 'log message') + if (item.name === 'budo' && item.type === 'connect') { + t.ok(item, 'budo start log message') + } next() }) - // 2 messages for each server run: 1 for server startup and 1 for bundle complete - t.plan(4) + // should only get two server startup messages + t.plan(2) // start a self-closing server, and then do it again (once) when it exits start().once('exit', start) From 7405986172eb9b86ddbb6e12a25036149061edda Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 17 Dec 2015 12:36:41 -0500 Subject: [PATCH 257/302] 7.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 165f44b..de8ea47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.0.3", + "version": "7.0.4", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 045613449ddc6b1c4aba511665ec6e70fe61a9f3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Dec 2015 16:20:45 -0500 Subject: [PATCH 258/302] use wg instead of w to avoid ambiguity with watch() function --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 3 ++- bin/help.txt | 3 ++- docs/api-usage.md | 16 +++++++++++++++- lib/budo.js | 3 ++- lib/parse-args.js | 4 +++- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df50d0..9c3b087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# 7.1.0 + +Added `--watch-glob` option which allows you to override the default watch glob without having to go through the `live()` / `watch()` API + +# 7.0.4 + +Small patch [#117](https://github.com/mattdesl/budo/pull/117) to fix a failing test in 7.0.3. + +# 7.0.3 + +- Bole no longer double-logs on shut-down and re-start. +- Fixed issue with request sizes being logged incorrectly in terminal + +# 7.0.1 .. 7.0.2 + +Small patches for [#110](https://github.com/mattdesl/budo/pull/110) and [#111](https://github.com/mattdesl/budo/pull/111). + # 7.0.0 Fixes previous patch and also updates to garnish redesign, leading to new log styles. diff --git a/README.md b/README.md index 12ab100..c10ad37 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ Options: --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding - --no-error-handler disable default DOM error handling + --no-error-handler disable default DOM error handling + --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' ``` By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. diff --git a/bin/help.txt b/bin/help.txt index 2bc38b1..84e1b93 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -21,4 +21,5 @@ Options: --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding - --no-error-handler disable default DOM error handling \ No newline at end of file + --no-error-handler disable default DOM error handling + --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' \ No newline at end of file diff --git a/docs/api-usage.md b/docs/api-usage.md index 69a669e..8866222 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -44,6 +44,9 @@ All settings are optional. - whether to set up a default LiveReload integration (see [LiveReload](#livereload)) - if a string is specified, only filenames matching that glob will trigger LiveReload events +- `watchGlob` (Array|String) + - a glob string or array of glob strings to use as the default when `opts.live` is specified, or when `live()` is called without arguments + - defaults to `'**/*.{html,css}'` - `open` (Boolean) - whether to launch the browser (default `false`) - `dir` (String|Array) @@ -184,7 +187,18 @@ If `opts.live` was not specified, and `b.watch()` was never set up, this event w # examples -#### LiveReload +#### http server + +A static HTTP server with no browserify capabilities, LiveReload integration, and a custom glob to watch for. + +```js +budo({ + live: true, + watchGlob: '{src,examples}/**/*.{html,css,js}' +}) +``` + +#### custom LiveReload Setting `opts.live` will provide a default configuration for live reloading. You can also specify a string to narrow the LiveReload triggers to a certain glob: diff --git a/lib/budo.js b/lib/budo.js index 2047afd..aa817d1 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -112,6 +112,7 @@ function createBudo (entries, opts) { }) } + var defaultWatchGlob = opts.watchGlob || '**/*.{html,css}' var server = createServer(middleware, opts) var closed = false var started = false @@ -172,7 +173,7 @@ function createBudo (entries, opts) { } else { // destroy previous if (fileWatcher) fileWatcher.close() - glob = glob && glob.length > 0 ? glob : '**/*.{html,css}' + glob = glob && glob.length > 0 ? glob : defaultWatchGlob glob = Array.isArray(glob) ? glob : [ glob ] watchOpt = xtend({ poll: opts.poll }, watchOpt) diff --git a/lib/parse-args.js b/lib/parse-args.js index 814d3da..d81c963 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -20,7 +20,8 @@ function parseArgs (args, opt) { 'dir', 'onupdate', 'serve', - 'title' + 'title', + 'watchGlob' ], default: module.exports.defaults, alias: { @@ -31,6 +32,7 @@ function parseArgs (args, opt) { dir: 'd', live: 'l', open: 'o', + watchGlob: [ 'wg', 'watch-glob' ], errorHandler: 'error-handler', 'live-port': ['L', 'livePort'], pushstate: 'P' From b2c6c32c113dcddfff852e5700db805712a5f028 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Dec 2015 16:20:45 -0500 Subject: [PATCH 259/302] add watch glob option --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 3 ++- bin/help.txt | 3 ++- docs/api-usage.md | 16 +++++++++++++++- lib/budo.js | 3 ++- lib/parse-args.js | 4 +++- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df50d0..9c3b087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# 7.1.0 + +Added `--watch-glob` option which allows you to override the default watch glob without having to go through the `live()` / `watch()` API + +# 7.0.4 + +Small patch [#117](https://github.com/mattdesl/budo/pull/117) to fix a failing test in 7.0.3. + +# 7.0.3 + +- Bole no longer double-logs on shut-down and re-start. +- Fixed issue with request sizes being logged incorrectly in terminal + +# 7.0.1 .. 7.0.2 + +Small patches for [#110](https://github.com/mattdesl/budo/pull/110) and [#111](https://github.com/mattdesl/budo/pull/111). + # 7.0.0 Fixes previous patch and also updates to garnish redesign, leading to new log styles. diff --git a/README.md b/README.md index 12ab100..c10ad37 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ Options: --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding - --no-error-handler disable default DOM error handling + --no-error-handler disable default DOM error handling + --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' ``` By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. diff --git a/bin/help.txt b/bin/help.txt index 2bc38b1..84e1b93 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -21,4 +21,5 @@ Options: --no-stream do not print messages to stdout --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding - --no-error-handler disable default DOM error handling \ No newline at end of file + --no-error-handler disable default DOM error handling + --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' \ No newline at end of file diff --git a/docs/api-usage.md b/docs/api-usage.md index 69a669e..8866222 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -44,6 +44,9 @@ All settings are optional. - whether to set up a default LiveReload integration (see [LiveReload](#livereload)) - if a string is specified, only filenames matching that glob will trigger LiveReload events +- `watchGlob` (Array|String) + - a glob string or array of glob strings to use as the default when `opts.live` is specified, or when `live()` is called without arguments + - defaults to `'**/*.{html,css}'` - `open` (Boolean) - whether to launch the browser (default `false`) - `dir` (String|Array) @@ -184,7 +187,18 @@ If `opts.live` was not specified, and `b.watch()` was never set up, this event w # examples -#### LiveReload +#### http server + +A static HTTP server with no browserify capabilities, LiveReload integration, and a custom glob to watch for. + +```js +budo({ + live: true, + watchGlob: '{src,examples}/**/*.{html,css,js}' +}) +``` + +#### custom LiveReload Setting `opts.live` will provide a default configuration for live reloading. You can also specify a string to narrow the LiveReload triggers to a certain glob: diff --git a/lib/budo.js b/lib/budo.js index 2047afd..aa817d1 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -112,6 +112,7 @@ function createBudo (entries, opts) { }) } + var defaultWatchGlob = opts.watchGlob || '**/*.{html,css}' var server = createServer(middleware, opts) var closed = false var started = false @@ -172,7 +173,7 @@ function createBudo (entries, opts) { } else { // destroy previous if (fileWatcher) fileWatcher.close() - glob = glob && glob.length > 0 ? glob : '**/*.{html,css}' + glob = glob && glob.length > 0 ? glob : defaultWatchGlob glob = Array.isArray(glob) ? glob : [ glob ] watchOpt = xtend({ poll: opts.poll }, watchOpt) diff --git a/lib/parse-args.js b/lib/parse-args.js index 814d3da..d81c963 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -20,7 +20,8 @@ function parseArgs (args, opt) { 'dir', 'onupdate', 'serve', - 'title' + 'title', + 'watchGlob' ], default: module.exports.defaults, alias: { @@ -31,6 +32,7 @@ function parseArgs (args, opt) { dir: 'd', live: 'l', open: 'o', + watchGlob: [ 'wg', 'watch-glob' ], errorHandler: 'error-handler', 'live-port': ['L', 'livePort'], pushstate: 'P' From 6c24307faa1edf40b3d3ddd46bac90256b6e5032 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Dec 2015 16:23:58 -0500 Subject: [PATCH 260/302] 7.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de8ea47..c86e709 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.0.4", + "version": "7.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 822ee0350a3c069d4ca219a2f19049f509ea9ed3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 21 Dec 2015 16:56:21 -0500 Subject: [PATCH 261/302] fix typo in api docs --- docs/api-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-usage.md b/docs/api-usage.md index 8866222..d917b36 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -94,7 +94,7 @@ For example, running the following script from the command line would behave lik var args = process.argv.slice(2) var babelify = require('babelify') -var budo = require('budo')(args, { +var budo = require('budo').cli(args, { // additional overrides for our custom tool pushstate: true, browserify: { From 1b1001764ca8fd4fd9c62a8b089fbabe5854e6e1 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 6 Jan 2016 10:41:42 -0500 Subject: [PATCH 262/302] add docs on HMR --- README.md | 2 +- docs/command-line-usage.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c10ad37..14d393e 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ By default, budo pretty-prints to terminal with [garnish](https://github.com/mat
-See [docs](#docs) for more details. PRs/suggestions/comments welcome. +See [docs](#docs) for more details and integrations, such as [React Hot Module Replacement](./docs/command-line-usage.md#hot-module-replacement). PRs/suggestions/comments welcome. ## features diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index 3d9484d..b80ef24 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -159,3 +159,21 @@ It is suggested you add a `` in your `index.html` for this to work with ne ``` + +## hot module replacement + +The following can integrate easily with budo: + +- Generic HMR: [browserify-hmr](https://github.com/AgentME/browserify-hmr) +- React: [livereactload](https://github.com/milankinen/livereactload) +- Vue: [vueify](https://github.com/vuejs/vueify) (to be used with `browserify-hmr`) + +You can usually follow the steps in those tools, except instead of using `watchify`, we will use `budo` and pass our browserify options after a full stop `--`. + +Example with [livereactload](https://github.com/milankinen/livereactload): + +```sh +budo index.js:bundle.js -- -t babelify -p livereactload +``` + +Make sure to disable the `--live` flag, otherwise it will trigger hard reloads. \ No newline at end of file From 4688a643e11bb7f14d012a55de4fde1a9aa1e21e Mon Sep 17 00:00:00 2001 From: mattdesl Date: Wed, 6 Jan 2016 10:42:25 -0500 Subject: [PATCH 263/302] update docs --- docs/command-line-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/command-line-usage.md b/docs/command-line-usage.md index b80ef24..545a3c4 100644 --- a/docs/command-line-usage.md +++ b/docs/command-line-usage.md @@ -176,4 +176,4 @@ Example with [livereactload](https://github.com/milankinen/livereactload): budo index.js:bundle.js -- -t babelify -p livereactload ``` -Make sure to disable the `--live` flag, otherwise it will trigger hard reloads. \ No newline at end of file +Make sure you don't pass a `--live` flag to budo, otherwise it will trigger hard reloads on file save. \ No newline at end of file From f8b076822224514b77e59b7b5156b4a329f12163 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 14:47:14 -0500 Subject: [PATCH 264/302] refactor server code to use middleware stacking --- docs/api-usage.md | 8 +- example/index.html | 1 + lib/lr-script-injector.js | 38 +++++++ lib/middleware.js | 128 +++++++++++++++++++++++ lib/server.js | 205 ++----------------------------------- lib/simple-http-logger.js | 49 +++++++++ package.json | 9 +- test/test-custom-server.js | 29 ++++-- test/test-live.js | 2 +- 9 files changed, 250 insertions(+), 219 deletions(-) create mode 100644 lib/lr-script-injector.js create mode 100644 lib/middleware.js create mode 100644 lib/simple-http-logger.js diff --git a/docs/api-usage.md b/docs/api-usage.md index d917b36..2056bf4 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -67,8 +67,8 @@ All settings are optional. - `browserifyArgs` (Array) - an array of command-line arguments passed to browserify - if specified, this will be used to construct the new instance -- `middleware` (Function) - - an optional `fn(req, res, next)` function for the server which is run before other routes +- `middleware` (Array|Function) + - an optional function or array of `fn(req, res, next)` functions for the server which is run before other routes; using `connect` style middleware - `errorHandler` (Boolean|Function) - whether to include a DOM-based reporter build/syntax errors (default `true`) - can be a `reporter(err)` function which takes an Error and returns the new bundle contents @@ -237,7 +237,7 @@ app #### middleware -Using `middleware` to create a small non-static server. +Using `middleware` to create a small non-static server. This can be an array of functions, or just a single function. ```js var url = require('url') @@ -248,7 +248,7 @@ var app = budo('./app.js', { res.statusCode = 200 res.end('hello world') } else { - // fall through to other budo routes + // fall through to other routes next() } } diff --git a/example/index.html b/example/index.html index 9ddaf55..7136b5c 100644 --- a/example/index.html +++ b/example/index.html @@ -2,6 +2,7 @@ + budo diff --git a/lib/lr-script-injector.js b/lib/lr-script-injector.js new file mode 100644 index 0000000..87d3fb1 --- /dev/null +++ b/lib/lr-script-injector.js @@ -0,0 +1,38 @@ +// TODO: Replace the inject-lr-script with this. +var respModifier = require('resp-modifier') +var path = require('path') + +module.exports = injectLiveReloadSnippet +function injectLiveReloadSnippet (opts) { + opts = opts || {} + + var modifier = respModifier({ + rules: [ + { match: //i, fn: prepend } + ] + }) + + var fn = function (req, res, next) { + var ext = path.extname(req.url) + if (!ext || /\.html?$/i.test(ext)) { + if (!req.headers.accept) { + req.headers.accept = 'text/html' + } + } + modifier(req, res, next) + } + + fn.host = (opts.host || 'localhost').split(':')[0] + fn.port = opts.port || 35729 + + function snippet () { + var src = '//' + fn.host + ':' + fn.port + '/livereload.js?snipver=1' + return '' + } + + function prepend (req, res, body) { + return body + snippet() + } + + return fn +} diff --git a/lib/middleware.js b/lib/middleware.js new file mode 100644 index 0000000..68d94e0 --- /dev/null +++ b/lib/middleware.js @@ -0,0 +1,128 @@ +// TODO: Expose this like webpack-dev-server middleware +var stacked = require('stacked') +var serveStatic = require('serve-static') +var defaultIndex = require('simple-html-index') +var logger = require('./simple-http-logger') +var urlLib = require('url') +var pushState = require('connect-pushstate') +var liveReload = require('./lr-script-injector') +var urlTrim = require('url-trim') + +module.exports = budoMiddleware +function budoMiddleware (entryMiddleware, opts) { + opts = opts || {} + var staticPaths = [].concat(opts.dir).filter(Boolean) + if (staticPaths.length === 0) { + staticPaths = [ process.cwd() ] + } + + var entrySrc = opts.serve + var live = opts.live + var handler = stacked() + var middlewares = [].concat(opts.middleware).filter(Boolean) + + // Everything is logged except favicon.ico + var logHandler = logger({ + ignore: [ '/favicon.ico' ] + }) + handler.use(function (req, res, next) { + logHandler(req, res, next) + }) + + // User middleware(s) can override others + middlewares.forEach(function (middleware) { + if (typeof middleware !== 'function') { + throw new Error('middleware options must be functions') + } + handler.use(function (req, res, next) { + logHandler.type = 'middleware' + middleware(req, res, next) + }) + }) + + // Re-route for pushState support + if (opts.pushstate) handler.use(pushState()) + + // Inject liveReload snippet on response + var liveInjector = liveReload() + handler.use(function (req, res, next) { + if (!live || live.plugin) return next() + if (live.host) liveInjector.host = live.host + if (live.port) liveInjector.port = live.port + liveInjector(req, res, next) + }) + + // Entry (watchify) middleware + if (entryMiddleware) { + var entryRoute = '/' + urlLib.parse(entrySrc).pathname + handler.use(function (req, res, next) { + if (urlTrim(req.url) === urlTrim(entryRoute)) { + entryMiddleware(req, res, next) + } else { + next() + } + }) + } + + // Ignore favicon clutter + handler.mount('/favicon.ico', favicon) + + // If the user wishes to *always* serve + // a generated index instead of a static one. + if (opts.forceDefaultIndex) { + handler.use(indexHandler) + } + + // Static assets (html/images/etc) + staticPaths.forEach(function (rootFile) { + var staticHandler = serveStatic(rootFile) + handler.use(function (req, res, next) { + logHandler.type = 'static' + staticHandler(req, res, next) + }) + }) + + // Generates a default index.html + // when none is found locally. + handler.use(indexHandler) + + // Handle errors + handler.use(function (req, res) { + res.statusCode = 404 + res.end('404 not found: ' + req.url) + }) + + // Allow live options to be changed at runtime + handler.setLiveOptions = setLiveOptions + return handler + + function setLiveOptions (opts) { + live = opts + } + + function favicon (req, res) { + var maxAge = 345600 // 4 days + res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(maxAge / 1000)) + res.setHeader('Content-Type', 'image/x-icon') + res.statusCode = 200 + res.end() + } + + function indexHandler (req, res, next) { + if (req.url === '/' || /\/index.html?/i.test(req.url)) { + // If we reach this, our response will be generated + // (not static from local file system) + logHandler.type = 'generated' + res.setHeader('content-type', 'text/html') + + var stream = opts.defaultIndex || defaultIndex + stream({ + entry: entrySrc, + title: opts.title, + css: opts.css + }).pipe(res) + } else { + next() + } + } +} diff --git a/lib/server.js b/lib/server.js index 60647f4..3fa125f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,203 +1,10 @@ +// TODO: https support var http = require('http') -var path = require('path') -var defaultIndex = require('simple-html-index') -var ecstatic = require('ecstatic') -var Router = require('routes-router') -var inject = require('inject-lr-script') -var urlParse = require('url').parse -var fs = require('fs') -var log = require('bole')('budo') -var asyncDetectSeries = require('async').detectSeries - -module.exports = createServer -function createServer (entryHandler, opts) { - var router = Router() - var staticPaths = [].concat(opts.dir).filter(Boolean) - if (staticPaths.length === 0) { - staticPaths = [ process.cwd() ] - } - - var staticHandlers = staticPaths.map(function (filepath, i, list) { - var last = i === list.length - 1 - return ecstatic({ root: filepath, handleError: last }) - }) - - var entrySrc = opts.serve - var live = opts.live - var middleware = opts.middleware || defaultMiddleware - if (typeof middleware !== 'function') { - throw new TypeError('expected opts.middleware to be a function') - } - - var server = http.createServer(function (req, res) { - if (middleware.length === 3) { - var removeLogger = logger('middleware', req, res) - // user wants to specify which routes to fall through to budo - middleware(req, res, function (err) { - removeLogger() - if (err) { - var msg = err.message ? err.message : err - console.error(msg) - res.statusCode = 400 - res.end(msg) - } else { - router(req, res) - } - }) - } else { - // all routes will fall through to budo routes - // we won't add a logger here to avoid doubling up - middleware(req, res) - router(req, res) - } - }) - - if (entryHandler) { - var entryRoute = urlParse(entrySrc).pathname - router.addRoute('/' + entryRoute, function (req, res) { - logger('generated', req, res) - entryHandler(req, res) - }) - } - - router.addRoute('/index.html', home) - router.addRoute('/', home) - router.addRoute('/favicon.ico', favicon) - router.addRoute('*.html?', wildcard(true)) - if (!opts.pushstate) { - router.addRoute('/**/', wildcard(true)) - } - router.addRoute('*', wildcard()) - - // allow user to toggle live reload integration - server.setLiveOptions = setLiveOptions +var createMiddleware = require('./middleware') +module.exports = function createServer (entryMiddleware, opts) { + var handler = createMiddleware(entryMiddleware, opts) + var server = http.createServer(handler) + server.setLiveOptions = handler.setLiveOptions return server - - function logger (type, req, res) { - var byteLength = 0 - var now = Date.now() - var fn = function () { - var elapsed = Date.now() - now - log.info({ - elapsed: elapsed, - contentLength: byteLength, - method: (req.method || 'GET').toUpperCase(), - url: req.url, - statusCode: res.statusCode, - type: type === 'static' ? undefined : type, - colors: { - elapsed: elapsed > 1000 ? 'yellow' : 'dim' - } - }) - } - res.once('finish', fn) - - var write = res.write - - // catch content-length of payload - res.write = function (payload) { - if (payload) byteLength += payload.length - res.write = write - res.write.apply(res, arguments) - } - - // allow removal - return function () { - res.removeListener('finish', fn) - } - } - - function favicon (req, res) { - var maxAge = 345600 // 4 days - res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(maxAge / 1000)) - res.setHeader('Content-Type', 'image/x-icon') - res.statusCode = 200 - // should we clutter logs with favicon.ico requests? - // logger('generated', req, res) - res.end() - } - - function defaultMiddleware (req, res, next) { - next() - } - - function setLiveOptions (liveOpts) { - live = liveOpts - } - - function wildcard (html) { - return function (req, res) { - // inject LiveReload into HTML content if needed - if (html && live && !live.plugin) { - res = inject(res, live) - } - logger('static', req, res) - staticRequest(req, res) - } - } - - function staticRequest (req, res, stack) { - if (!stack) { - stack = staticHandlers.slice() - } - var nextFn = stack.shift() - nextFn(req, res, function () { - if (stack.length === 0) { - if (opts.pushstate) { - // reset for home - req.url = '/' - res.statusCode = 200 - home(req, res) - } else { - res.statusCode = 404 - res.end('404 not found: ' + req.url) - } - } else { - staticRequest(req, res, stack) - } - }) - } - - function findDefaultIndex (cb) { - if (opts.forceDefaultIndex) { - return process.nextTick(function () { - cb(false) - }) - } - - var homeFiles = staticPaths.map(function (base) { - return path.join(base, 'index.html') - }) - asyncDetectSeries(homeFiles, fs.exists, cb) - } - - function home (req, res) { - findDefaultIndex(function (result) { - // inject LiveReload into HTML content if needed - if (live && !live.plugin) { - res = inject(res, live) - } - - var type = result ? 'static' : 'generated' - logger(type, req, res) - - if (result) { - staticRequest(req, res) - } else { - generateIndex(req, res) - } - }) - } - - function generateIndex (req, res) { - res.setHeader('content-type', 'text/html') - - var stream = opts.defaultIndex || defaultIndex - stream({ - entry: entrySrc, - title: opts.title, - css: opts.css - }).pipe(res) - } } diff --git a/lib/simple-http-logger.js b/lib/simple-http-logger.js new file mode 100644 index 0000000..d4798e7 --- /dev/null +++ b/lib/simple-http-logger.js @@ -0,0 +1,49 @@ +var log = require('bole')('budo') + +module.exports = simpleHttpLoggerMiddleware +function simpleHttpLoggerMiddleware (opts) { + opts = opts || {} + var ignores = [].concat(opts.ignore).filter(Boolean) + + var httpLogger = function simpleHttpLogger (req, res, next) { + if (ignores.indexOf(req.url) >= 0) return next() + if (!req.url) return next() + + var byteLength = 0 + var now = Date.now() + var onFinished = function () { + var elapsed = Date.now() - now + log.info({ + elapsed: elapsed, + contentLength: byteLength, + method: (req.method || 'GET').toUpperCase(), + url: req.url, + statusCode: res.statusCode, + type: httpLogger.type === 'static' ? undefined : httpLogger.type, + colors: { + elapsed: elapsed > 1000 ? 'yellow' : 'dim' + } + }) + } + + var isAlreadyLogging = res._simpleHttpLogger + res._simpleHttpLogger = true + + if (!isAlreadyLogging) { + var write = res.write + res.once('finish', onFinished) + + // catch content-length of payload + res.write = function (payload) { + if (payload) byteLength += payload.length + res.write = write + res.write.apply(res, arguments) + } + } + + next() + } + + httpLogger.type = 'static' + return httpLogger +} diff --git a/package.json b/package.json index c86e709..18194be 100644 --- a/package.json +++ b/package.json @@ -13,27 +13,28 @@ "url": "https://github.com/mattdesl" }, "dependencies": { - "async": "^1.5.0", "bole": "^2.0.0", "browserify": "^12.0.1", "chokidar": "^1.0.1", - "ecstatic": "^1.0.0", + "connect-pushstate": "^1.0.0", "events": "^1.0.2", "garnish": "^5.0.0", "get-ports": "^1.0.2", - "inject-lr-script": "^1.0.0", "internal-ip": "^1.0.1", "micromatch": "^2.2.0", "minimist": "^1.1.0", "once": "^1.3.2", "opn": "^3.0.2", "resolve": "^1.1.6", - "routes-router": "^4.1.2", + "resp-modifier": "^6.0.0", + "serve-static": "^1.10.0", "simple-html-index": "^1.1.0", + "stacked": "^1.1.1", "stdout-stream": "^1.4.0", "strip-ansi": "^3.0.0", "term-color": "^1.0.1", "tiny-lr": "^0.2.0", + "url-trim": "^1.0.0", "watchify-middleware": "^1.3.0", "xtend": "^4.0.0" }, diff --git a/test/test-custom-server.js b/test/test-custom-server.js index b369085..3acf0a1 100644 --- a/test/test-custom-server.js +++ b/test/test-custom-server.js @@ -5,7 +5,7 @@ var request = require('request') var file = path.join(__dirname, 'fixtures', 'app.js') -test('custom middleware with next()', function (t) { +test('custom middleware', function (t) { t.plan(1) var b = budo(file, { middleware: middleware @@ -28,21 +28,28 @@ test('custom middleware with next()', function (t) { } }) -test('custom middleware without next()', function (t) { - t.plan(3) +test('stacking middlewares', function (t) { + t.plan(2) var b = budo(file, { - middleware: middleware, + middleware: [ + function (req, res, next) { + t.equal(req.url, '/api') + next() + }, + function (req, res, next) { + if (req.url === '/api') { + res.end('api contents') + } else { + next() + } + } + ], serve: 'bundle.js' }).on('connect', function (ev) { - request.get({ uri: ev.uri + 'bundle.js' }, function (err, resp, body) { + request.get({ uri: ev.uri + 'api' }, function (err, resp, body) { b.close() if (err) return t.fail(err) - t.equal(resp.statusCode, 200, 'status code 200') - t.equal(resp.headers['content-type'], 'application/javascript; charset=utf-8', 'gets bundle.js') + t.equal(body, 'api contents') }) }) - - function middleware (req) { - t.equal(req.url, '/bundle.js', 'middleware reached') - } }) diff --git a/test/test-live.js b/test/test-live.js index 2432db4..9024294 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -112,5 +112,5 @@ function getHTMLNoLive () { } function getHTML () { - return 'budo' + return 'budo' } From 23da9e266ccf1cab7a203a04c2b3e7daf14a6e1c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 14:48:15 -0500 Subject: [PATCH 265/302] pushstate run example --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 18194be..32ecee8 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "scripts": { "test": "standard && tape test/test*.js | tap-spec", "start": "./bin/cmd.js example/app.js:bundle.js -v --dir example -- -t babelify", + "pushstate": "./bin/cmd.js example/app.js:bundle.js -v --dir example --live --pushstate -- -t babelify", "live": "./bin/cmd.js example/app.js:bundle.js --dir example --verbose --live -- -t babelify" }, "keywords": [ From 4cebf58f23643e7d77c918a763db03981bfd67d5 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 14:49:34 -0500 Subject: [PATCH 266/302] small code cleanup --- lib/lr-script-injector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lr-script-injector.js b/lib/lr-script-injector.js index 87d3fb1..f7cfb38 100644 --- a/lib/lr-script-injector.js +++ b/lib/lr-script-injector.js @@ -22,7 +22,7 @@ function injectLiveReloadSnippet (opts) { modifier(req, res, next) } - fn.host = (opts.host || 'localhost').split(':')[0] + fn.host = opts.host || 'localhost' fn.port = opts.port || 35729 function snippet () { From 2d89197a5c3b560a508b6ebb7ac21496cc1645c7 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 14:56:01 -0500 Subject: [PATCH 267/302] document colon operator in main block of examples --- README.md | 3 +++ docs/api-usage.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14d393e..4f1378b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ budo index.js --open # enable LiveReload on HTML/CSS/JS file changes budo index.js --live +# default html will use src="static/bundle.js" +budo src/index.js:static/bundle.js + # pass some options to browserify budo index.js --live -- -t babelify ``` diff --git a/docs/api-usage.md b/docs/api-usage.md index 2056bf4..316608d 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -78,10 +78,10 @@ All settings are optional. - `verbose` (Boolean) - also print `'debug'` level messages to garnish; such as the pending state of the bundle and how many files changed in the last update. - `defaultIndex` (Function) - - a function `fn(params)` that returns a Readable stream, takes the following `params`: + - a function `fn(params, req)` that returns a Readable stream, takes the following `params`: `{ entry: opts.serve, title: opts.title, css: opts.css }` - defaults to [simple-html-index](https://github.com/mattdesl/simple-html-index) - + ### `b = budo.cli(args[, opts])` Runs budo as a command-line tool, from the specified array of arguments and an optional `opts` object for overrides. The options are the same as above. From ddc52b267b9e13bed47e0c270e49a7fc2bd13ec6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 15:09:52 -0500 Subject: [PATCH 268/302] small tweaks and new live test for address --- lib/middleware.js | 2 +- test/test-live.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/middleware.js b/lib/middleware.js index 68d94e0..1de1125 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -109,7 +109,7 @@ function budoMiddleware (entryMiddleware, opts) { } function indexHandler (req, res, next) { - if (req.url === '/' || /\/index.html?/i.test(req.url)) { + if (urlLib.parse(req.url).pathname === '/' || /\/index.html?/i.test(req.url)) { // If we reach this, our response will be generated // (not static from local file system) logHandler.type = 'generated' diff --git a/test/test-live.js b/test/test-live.js index 9024294..07e22bc 100644 --- a/test/test-live.js +++ b/test/test-live.js @@ -4,6 +4,7 @@ var path = require('path') var request = require('request') var fs = require('fs') var source = fs.readFileSync(path.join(__dirname, 'fixtures', 'app.js'), 'utf8') +var internalIp = require('internal-ip') test('should inject LiveReload snippet', function (t) { t.plan(4) @@ -98,6 +99,32 @@ test('should not inject LiveReload snippet', function (t) { }) }) +test('LiveReload snippet host should default to budo host', function (t) { + t.plan(2) + t.timeoutAfter(10000) + + var addr = internalIp() + var entry = path.join(__dirname, 'fixtures', 'app.js') + var app = budo(entry, { + dir: __dirname, + port: 8000, + host: addr, + serve: 'app.js' + }) + .live() + .on('error', function (err) { + t.fail(err) + }) + .on('connect', function (ev) { + matchesHTML(t, ev.uri, getHTMLWithHost(addr), function () { + app.close() + }) + }) + .on('exit', function () { + t.ok(true, 'closing') + }) +}) + function matchesHTML (t, uri, html, cb) { request.get({ uri: uri + 'index.html' }, function (err, resp, body) { if (err) t.fail(err) @@ -114,3 +141,7 @@ function getHTMLNoLive () { function getHTML () { return 'budo' } + +function getHTMLWithHost (ip) { + return 'budo' +} From d5438957955c6ad3b208c01d99e31969684be8be Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 15:20:12 -0500 Subject: [PATCH 269/302] document new breaking change --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c3b087..1ea87e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 8.0.0 + +The server code has been refactored to use connect/express-style middleware stacking. Fixes [#80](https://github.com/mattdesl/budo/issues/80), [#79](https://github.com/mattdesl/budo/issues/79), [#124](https://github.com/mattdesl/budo/issues/124), [#128](https://github.com/mattdesl/budo/issues/128). + +##### Major Changes + +Functions for `opts.middleware` now assumes the following signature, and will not behave differently based on the number of arguments you specify: + + - `middleware(req, res, next)` + +##### Minor Changes + +The `middleware` options can now be an array of functions, or a single function. + # 7.1.0 Added `--watch-glob` option which allows you to override the default watch glob without having to go through the `live()` / `watch()` API From 2053ccd90db93fb4d9692565ba951997eeaa7d11 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 18 Jan 2016 15:20:18 -0500 Subject: [PATCH 270/302] 8.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32ecee8..b447e0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "7.1.0", + "version": "8.0.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 32b2a6c63cd47afb57db59f8fe975a069525823c Mon Sep 17 00:00:00 2001 From: ashnur Date: Tue, 26 Jan 2016 15:15:54 +0100 Subject: [PATCH 271/302] http://stackoverflow.com/a/1732454/565303 I mean, what if I have a class in my body tag? --- lib/lr-script-injector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lr-script-injector.js b/lib/lr-script-injector.js index f7cfb38..0514918 100644 --- a/lib/lr-script-injector.js +++ b/lib/lr-script-injector.js @@ -8,7 +8,7 @@ function injectLiveReloadSnippet (opts) { var modifier = respModifier({ rules: [ - { match: //i, fn: prepend } + { match: /]*>/i, fn: prepend } ] }) From f15e46ca059507f5d6f5c59e8078ef9c47cfa075 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Jan 2016 10:19:15 -0500 Subject: [PATCH 272/302] 8.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b447e0e..539823c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.0.0", + "version": "8.0.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 6b9e2c556dba7ee39b8d7523faff5bdc79c54b39 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Jan 2016 17:58:57 -0500 Subject: [PATCH 273/302] ensure bundling starts after server connects --- lib/budo.js | 4 ++++ lib/bundler.js | 1 + test/test-event-order.js | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 test/test-event-order.js diff --git a/lib/budo.js b/lib/budo.js index aa817d1..d654d70 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -275,6 +275,10 @@ function createBudo (entries, opts) { dir: opts.dir }) + // initial bundle should come after + // connect event! + if (bundler) bundler.bundle() + // launch browser if (opts.open) { openUrl(uri) diff --git a/lib/bundler.js b/lib/bundler.js index b7e32b8..7c09a6f 100644 --- a/lib/bundler.js +++ b/lib/bundler.js @@ -34,6 +34,7 @@ function createBundler (files, opts) { return createMiddleware(bundler, { delay: opts.delay || 0, + initialBundle: false, errorHandler: errorHandler }) } diff --git a/test/test-event-order.js b/test/test-event-order.js new file mode 100644 index 0000000..67d4fce --- /dev/null +++ b/test/test-event-order.js @@ -0,0 +1,24 @@ +var test = require('tape') +var budo = require('../') +var path = require('path') + +test('event order should be correct', function (t) { + t.plan(1) + + var fixture = path.resolve(__dirname, 'fixtures', 'app.js') + var hasConnected = false + var hasUpdated = false + var app = budo(fixture) + .once('connect', function () { + if (hasUpdated) { + app.close() + t.fail('update before connect') + } + hasConnected = true + }) + .once('update', function () { + hasUpdated = true + t.equal(hasConnected, true, 'got update after connect') + app.close() + }) +}) From 7cc3cc5a11eecfd6c6c687e749e2ac027df7ce1a Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Jan 2016 18:08:27 -0500 Subject: [PATCH 274/302] use mapEntry for windows tests --- test/test-bundle.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test-bundle.js b/test/test-bundle.js index bb807fc..9a3e9a0 100644 --- a/test/test-bundle.js +++ b/test/test-bundle.js @@ -6,6 +6,7 @@ var xtend = require('xtend') var browserify = require('browserify') var path = require('path') var vm = require('vm') +var mapEntry = require('../lib/map-entry') test('serves app.js', run('test/fixtures/app.js')) test('entry mapping to bundle.js', run('test/fixtures/app.js:bundle.js')) @@ -101,7 +102,7 @@ function matches (t, entries, opt) { debug: opt.debug }, opt.browserify)) entries.forEach(function (entry) { - entry = entry.split(':')[0] + entry = mapEntry(entry).from b.add(path.resolve(entry)) }) From 045e33cd9e719dca174e8795ad5ac87064cabcfb Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 26 Jan 2016 18:17:36 -0500 Subject: [PATCH 275/302] fixing windows tests --- test/test-api.js | 2 +- test/test-onupdate.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-api.js b/test/test-api.js index e547bbf..10b7591 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -77,7 +77,7 @@ test('entry mapping', function (t) { test('--serve allows explicit bundle renaming', function (t) { t.plan(2) - t.timeoutAfter(2000) + t.timeoutAfter(5000) var app = budo(['test/fixtures/app', 'test/fixtures/with space.js'], { serve: 'static/foo.js' diff --git a/test/test-onupdate.js b/test/test-onupdate.js index c8fb40e..57f5121 100644 --- a/test/test-onupdate.js +++ b/test/test-onupdate.js @@ -12,7 +12,7 @@ test('should trigger echo', function (t) { var proc = spawn(cli, [ src, '--onupdate', 'echo FOO BAR', '--no-stream' ]) proc.stdout.on('data', function (buf) { - t.equal(buf.toString(), 'FOO BAR\n') + t.equal(buf.toString().trim(), 'FOO BAR') proc.on('exit', function () { t.ok(true, 'closed') }) From e3b857a9159bff70527fd5489abc8cc6f30f2dbf Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Jan 2016 18:20:52 -0500 Subject: [PATCH 276/302] 8.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 539823c..2fd94d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.0.1", + "version": "8.0.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 5f324d5b1666fbfe3e17366e306e91d5336378c3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 1 Feb 2016 09:34:18 -0500 Subject: [PATCH 277/302] fix opts.live from CLI and also allow array to be passed --- lib/budo.js | 5 +++-- lib/parse-args.js | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/budo.js b/lib/budo.js index d654d70..f439f0c 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -3,7 +3,7 @@ var xtend = require('xtend') var once = require('once') var path = require('path') var EventEmitter = require('events').EventEmitter -var isMatch = require('micromatch').isMatch +var isMatch = require('micromatch') var openUrl = require('opn') var internalIp = require('internal-ip') var garnish = require('garnish') @@ -153,7 +153,8 @@ function createBudo (entries, opts) { function defaultFileEvent (file) { var filename = path.basename(file) - if (typeof opts.live === 'string' && !isMatch(filename, opts.live)) { + if ((Array.isArray(opts.live) || typeof opts.live === 'string') && + isMatch(filename, opts.live).length === 0) { return } emitter.reload(file) diff --git a/lib/parse-args.js b/lib/parse-args.js index d81c963..de3a367 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -8,7 +8,6 @@ function parseArgs (args, opt) { 'stream', 'debug', 'errorHandler', - 'live', 'open', 'portfind', 'ndjson', From 3a613c841995b97ec1cecab343f034911bd36c09 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 1 Feb 2016 09:34:25 -0500 Subject: [PATCH 278/302] 8.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2fd94d9..4ff367e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.0.2", + "version": "8.0.3", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 063a2e6eadda9d150560a586042c918e797a7047 Mon Sep 17 00:00:00 2001 From: Ben Drucker Date: Fri, 5 Feb 2016 09:11:54 -0800 Subject: [PATCH 279/302] Bump watchify-middleware to 1.6 (w/ bundle method) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ff367e..a752af5 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "term-color": "^1.0.1", "tiny-lr": "^0.2.0", "url-trim": "^1.0.0", - "watchify-middleware": "^1.3.0", + "watchify-middleware": "^1.6.0", "xtend": "^4.0.0" }, "devDependencies": { From 61a2f999412881c7bc69628dba6c1d1dee23933c Mon Sep 17 00:00:00 2001 From: mattdesl Date: Fri, 5 Feb 2016 13:21:41 -0500 Subject: [PATCH 280/302] 8.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a752af5..38f4ee9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.0.3", + "version": "8.0.4", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From eefa3fbe05bb3d5d0dc696b7ab7b6fb630cdd4d6 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Thu, 11 Feb 2016 14:03:51 -0500 Subject: [PATCH 281/302] split out module inject-lr-script --- lib/lr-script-injector.js | 38 -------------------------------------- lib/middleware.js | 2 +- package.json | 1 + 3 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 lib/lr-script-injector.js diff --git a/lib/lr-script-injector.js b/lib/lr-script-injector.js deleted file mode 100644 index 0514918..0000000 --- a/lib/lr-script-injector.js +++ /dev/null @@ -1,38 +0,0 @@ -// TODO: Replace the inject-lr-script with this. -var respModifier = require('resp-modifier') -var path = require('path') - -module.exports = injectLiveReloadSnippet -function injectLiveReloadSnippet (opts) { - opts = opts || {} - - var modifier = respModifier({ - rules: [ - { match: /]*>/i, fn: prepend } - ] - }) - - var fn = function (req, res, next) { - var ext = path.extname(req.url) - if (!ext || /\.html?$/i.test(ext)) { - if (!req.headers.accept) { - req.headers.accept = 'text/html' - } - } - modifier(req, res, next) - } - - fn.host = opts.host || 'localhost' - fn.port = opts.port || 35729 - - function snippet () { - var src = '//' + fn.host + ':' + fn.port + '/livereload.js?snipver=1' - return '' - } - - function prepend (req, res, body) { - return body + snippet() - } - - return fn -} diff --git a/lib/middleware.js b/lib/middleware.js index 1de1125..5a27e5e 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -5,7 +5,7 @@ var defaultIndex = require('simple-html-index') var logger = require('./simple-http-logger') var urlLib = require('url') var pushState = require('connect-pushstate') -var liveReload = require('./lr-script-injector') +var liveReload = require('inject-lr-script') var urlTrim = require('url-trim') module.exports = budoMiddleware diff --git a/package.json b/package.json index 38f4ee9..e544b1f 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "events": "^1.0.2", "garnish": "^5.0.0", "get-ports": "^1.0.2", + "inject-lr-script": "^2.0.0", "internal-ip": "^1.0.1", "micromatch": "^2.2.0", "minimist": "^1.1.0", From ef4352a1de7f9f125e7a8f9203b14c829c9a093f Mon Sep 17 00:00:00 2001 From: mattdesl Date: Sat, 13 Feb 2016 15:15:17 -0500 Subject: [PATCH 282/302] update node travis versions --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2c1a73a..eb80bb5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,8 @@ node_js: - "0.8" - "0.10" - "0.12" + - "4.0" + - "4.1" + - "5.0" before_install: - npm install -g npm@~2.14.5 From 51d8c2e17d9983a9ecac0946b22740a38092a330 Mon Sep 17 00:00:00 2001 From: Julien Bisconti Date: Fri, 4 Mar 2016 21:29:11 +0100 Subject: [PATCH 283/302] --cors option added for Access-Control-Allow-Origin: * --- README.md | 5 ++++- docs/api-usage.md | 18 ++++++++++-------- index.js | 5 +++++ lib/middleware.js | 2 ++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4f1378b..ddd01f3 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ budo src/index.js:static/bundle.js # pass some options to browserify budo index.js --live -- -t babelify + +# set the header to use CORS (`Access-Control-Allow-Origin: *`) +budo index.js --live --cors ``` Then open [http://localhost:9966/](http://localhost:9966/) to see the content in action. @@ -97,7 +100,7 @@ Options: --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' ``` -By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. +By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. Everything after `--` is passed directly to browserify. Example: diff --git a/docs/api-usage.md b/docs/api-usage.md index 316608d..b206a9f 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -6,7 +6,7 @@ The API mirrors the CLI except you must provide a `stream` for logging, and it p Sets up a new instance of `budo`, where `entry` is a path or or array of paths. -`entry` can be optional -- if no entry paths are given, budo simply acts as a static HTTP server with optional LiveReload. +`entry` can be optional -- if no entry paths are given, budo simply acts as a static HTTP server with optional LiveReload. The return value is an event emitter. @@ -34,16 +34,18 @@ All settings are optional. - `port` (Number) - the base port to use for the development server (default `9966`) -- `livePort` (Number) +- `livePort` (Number) - the base port to use for the LiveReload server (default `35729`) -- `portfind` (Boolean) +- `portfind` (Boolean) - whether to use portfinding to find the next available ports (default `true`) - `host` (String) - the host to listen on (default `'localhost'`) -- `live` (Boolean|String) +- `live` (Boolean|String) - whether to set up a default LiveReload integration (see [LiveReload](#livereload)) - if a string is specified, only filenames matching that glob will trigger LiveReload events +- `cors` (Boolean) + - Set the header to use CORS (`Access-Control-Allow-Origin: *`) - `watchGlob` (Array|String) - a glob string or array of glob strings to use as the default when `opts.live` is specified, or when `live()` is called without arguments - defaults to `'**/*.{html,css}'` @@ -117,7 +119,7 @@ If `path` is undefined, this is treated as a hard page reload. #### `b.live([opt])` -If `live` was not specified, you can manually enable the LiveReload server with the specified `opt` options: +If `live` was not specified, you can manually enable the LiveReload server with the specified `opt` options: - `port` defaults to the `ev.livePort` from the `'connect'` event - `host` defaults to the `ev.host` from the `'connect'` event @@ -127,7 +129,7 @@ See [LiveReload](#livereload) for an example. #### `b.watch([globs, chokidarOpts])` -If `live` was not specified, you can manually enabe [chokidar's](https://github.com/paulmillr/chokidar) file watching with the specified `globs` (array or string) and options. +If `live` was not specified, you can manually enabe [chokidar's](https://github.com/paulmillr/chokidar) file watching with the specified `globs` (array or string) and options. `globs` defaults to watching `**/*.{html,css}`. @@ -233,7 +235,7 @@ app .on('update', function (buf) { console.log('bundle finished --> %d bytes', buf.length) }) -``` +``` #### middleware @@ -278,4 +280,4 @@ gulp.task('dev', function(cb) { }) ``` -Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and serve the browserified contents of `index.js` and write pretty-printed logs to `stdout`. \ No newline at end of file +Now running `gulp dev` will spin up a server on 9966, spawn watchify, and incrementally rebundle during development. It will stub out an `index.html` and serve the browserified contents of `index.js` and write pretty-printed logs to `stdout`. diff --git a/index.js b/index.js index 5004990..d75a8b2 100644 --- a/index.js +++ b/index.js @@ -51,6 +51,11 @@ function budoCLI (args, opts) { argv.live = argv.live === 'true' } + if (typeof argv.cors === 'string' && /(true|false)/.test(argv.cors)) { + argv.cors = argv.cors === 'true' + console.log('-------CORS...........') + } + // CLI only option for executing a child process var instance = budo(entries, argv).on('error', exit) var onUpdates = [].concat(argv.onupdate).filter(Boolean) diff --git a/lib/middleware.js b/lib/middleware.js index 5a27e5e..857267e 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -18,6 +18,7 @@ function budoMiddleware (entryMiddleware, opts) { var entrySrc = opts.serve var live = opts.live + var cors = opts.cors var handler = stacked() var middlewares = [].concat(opts.middleware).filter(Boolean) @@ -26,6 +27,7 @@ function budoMiddleware (entryMiddleware, opts) { ignore: [ '/favicon.ico' ] }) handler.use(function (req, res, next) { + if (cors) { res.setHeader('Access-Control-Allow-Origin', '*'); } logHandler(req, res, next) }) From 2abbaea01d92f920b302df33cf661faafd1760e4 Mon Sep 17 00:00:00 2001 From: Julien Bisconti Date: Fri, 4 Mar 2016 21:30:54 +0100 Subject: [PATCH 284/302] remove console.log --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index d75a8b2..bae8a26 100644 --- a/index.js +++ b/index.js @@ -53,7 +53,6 @@ function budoCLI (args, opts) { if (typeof argv.cors === 'string' && /(true|false)/.test(argv.cors)) { argv.cors = argv.cors === 'true' - console.log('-------CORS...........') } // CLI only option for executing a child process From 748f351815069ecc2d75332567fdd69d9cc27dec Mon Sep 17 00:00:00 2001 From: Julien Bisconti Date: Sat, 5 Mar 2016 08:05:18 +0100 Subject: [PATCH 285/302] remove extra semicolon --- lib/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/middleware.js b/lib/middleware.js index 857267e..a6c00b5 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -27,7 +27,7 @@ function budoMiddleware (entryMiddleware, opts) { ignore: [ '/favicon.ico' ] }) handler.use(function (req, res, next) { - if (cors) { res.setHeader('Access-Control-Allow-Origin', '*'); } + if (cors) { res.setHeader('Access-Control-Allow-Origin', '*') } logHandler(req, res, next) }) From 5d3cb5f443892b56ee8a2a2ea7ca65365d86e1b5 Mon Sep 17 00:00:00 2001 From: Julien Bisconti Date: Sat, 5 Mar 2016 08:42:17 +0100 Subject: [PATCH 286/302] add test for CORS --- test/test-server.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/test-server.js b/test/test-server.js index 27ad747..4a00382 100644 --- a/test/test-server.js +++ b/test/test-server.js @@ -110,3 +110,21 @@ function port (expected, opt) { .on('error', t.fail.bind(t)) } } + +test('serve with CORS enable', function (t) { + t.plan(2) + var app = budo(entry, { dir: __dirname, cors: true }) + .on('connect', function (ev) { + request.get({ + uri: ev.uri + 'favicon.ico' + }, function (err, resp) { + if (err) t.fail(err) + t.equal(resp.headers['access-control-allow-origin'], '*') + app.close() + }) + }) + .on('exit', function () { + t.ok(true, 'closed') + }) + .on('error', t.fail.bind(t)) +}) From db826955ed1d2b0f59f4b2a217b9bc7f1e925d76 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Mar 2016 09:05:05 -0500 Subject: [PATCH 287/302] cors tweaks --- README.md | 1 + index.js | 4 ---- lib/parse-args.js | 3 ++- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ddd01f3..e100731 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ Options: --poll=N use polling for file watch, with optional interval N --title optional title for default index.html --css optional stylesheet href for default index.html + --cors set header to use CORS (Access-Control-Allow-Origin: *) --ndjson print ndjson instead of pretty-printed logs --verbose, -v also include debug messages --no-stream do not print messages to stdout diff --git a/index.js b/index.js index bae8a26..5004990 100644 --- a/index.js +++ b/index.js @@ -51,10 +51,6 @@ function budoCLI (args, opts) { argv.live = argv.live === 'true' } - if (typeof argv.cors === 'string' && /(true|false)/.test(argv.cors)) { - argv.cors = argv.cors === 'true' - } - // CLI only option for executing a child process var instance = budo(entries, argv).on('error', exit) var onUpdates = [].concat(argv.onupdate).filter(Boolean) diff --git a/lib/parse-args.js b/lib/parse-args.js index de3a367..2380062 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -11,7 +11,8 @@ function parseArgs (args, opt) { 'open', 'portfind', 'ndjson', - 'verbose' + 'verbose', + 'cors' ], string: [ 'host', From b26bd9aae3463db64748a21e7befb9fdac9b5ee8 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Mar 2016 09:05:19 -0500 Subject: [PATCH 288/302] 8.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e544b1f..bc68f51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.0.4", + "version": "8.1.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 076152924cce52a9d862238a1e16b9fda78bea16 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Mar 2016 09:41:26 -0500 Subject: [PATCH 289/302] support ssl/https mode --- README.md | 7 +++++-- bin/help.txt | 4 ++++ docs/api-usage.md | 6 ++++++ lib/budo.js | 3 ++- lib/parse-args.js | 10 ++++++++-- lib/server.js | 12 ++++++++++-- 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e100731..e5183bd 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ budo src/index.js:static/bundle.js # pass some options to browserify budo index.js --live -- -t babelify -# set the header to use CORS (`Access-Control-Allow-Origin: *`) -budo index.js --live --cors +# use HTTPS and enable CORS headers +budo index.js --ssl --cors --cert=cert.pem --key=key.pem ``` Then open [http://localhost:9966/](http://localhost:9966/) to see the content in action. @@ -91,6 +91,9 @@ Options: --poll=N use polling for file watch, with optional interval N --title optional title for default index.html --css optional stylesheet href for default index.html + --ssl, -S create an HTTPS server instead of HTTP + --cert, -C the cert for SSL (default cert.pem) + --key, -K the key for SSL (default key.pem) --cors set header to use CORS (Access-Control-Allow-Origin: *) --ndjson print ndjson instead of pretty-printed logs --verbose, -v also include debug messages diff --git a/bin/help.txt b/bin/help.txt index 84e1b93..3e2f3c8 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -16,6 +16,10 @@ Options: --poll=N use polling for file watch, with optional interval N --title optional title for default index.html --css optional stylesheet href for default index.html + --ssl, -S create an HTTPS server instead of HTTP + --cert, -C the cert for SSL (default cert.pem) + --key, -K the key for SSL (default key.pem) + --cors set header to use CORS (Access-Control-Allow-Origin: *) --ndjson print ndjson instead of pretty-printed logs --verbose, -v also include debug messages --no-stream do not print messages to stdout diff --git a/docs/api-usage.md b/docs/api-usage.md index b206a9f..08b5ab4 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -46,6 +46,12 @@ All settings are optional. will trigger LiveReload events - `cors` (Boolean) - Set the header to use CORS (`Access-Control-Allow-Origin: *`) +- `ssl` (Boolean) + - Creates an HTTPS server instead of HTTP +- `cert` (String) + - The SSL public certificate file path (default `'cert.pem'`) +- `key` (String) + - The SSL private key file path (default `'key.pem'`) - `watchGlob` (Array|String) - a glob string or array of glob strings to use as the default when `opts.live` is specified, or when `live()` is called without arguments - defaults to `'**/*.{html,css}'` diff --git a/lib/budo.js b/lib/budo.js index f439f0c..2db48bc 100644 --- a/lib/budo.js +++ b/lib/budo.js @@ -257,7 +257,8 @@ function createBudo (entries, opts) { opts.host = getHostAddress(opts.host) var port = opts.port - var uri = 'http://' + opts.host + ':' + port + '/' + var protocol = opts.ssl ? 'https' : 'http' + var uri = protocol + '://' + opts.host + ':' + port + '/' log.info({ message: 'Server running at', url: uri, type: 'connect' }) diff --git a/lib/parse-args.js b/lib/parse-args.js index 2380062..ec2b0bc 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -12,7 +12,8 @@ function parseArgs (args, opt) { 'portfind', 'ndjson', 'verbose', - 'cors' + 'cors', + 'ssl' ], string: [ 'host', @@ -21,11 +22,16 @@ function parseArgs (args, opt) { 'onupdate', 'serve', 'title', - 'watchGlob' + 'watchGlob', + 'cert', + 'key' ], default: module.exports.defaults, alias: { port: 'p', + ssl: 'S', + cert: 'C', + key: 'K', verbose: 'v', help: 'h', host: 'H', diff --git a/lib/server.js b/lib/server.js index 3fa125f..94bebbf 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,10 +1,18 @@ -// TODO: https support var http = require('http') +var https = require('https') var createMiddleware = require('./middleware') +var fs = require('fs') module.exports = function createServer (entryMiddleware, opts) { + var httpsOpts = opts.ssl ? { + cert: fs.readFileSync(opts.cert || 'cert.pem'), + key: fs.readFileSync(opts.key || 'key.pem') + } : undefined + var handler = createMiddleware(entryMiddleware, opts) - var server = http.createServer(handler) + var server = httpsOpts + ? https.createServer(httpsOpts, handler) + : http.createServer(handler) server.setLiveOptions = handler.setLiveOptions return server } From 3e84b4d931061eaf4ebca55b4342f69bece96c77 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Mar 2016 09:42:11 -0500 Subject: [PATCH 290/302] 8.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc68f51..aa220a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.1.0", + "version": "8.2.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 9576d1526299757ffe6cc8b79f84ff74ed8e4d4d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 7 Mar 2016 09:45:33 -0500 Subject: [PATCH 291/302] update changelog --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea87e1..ddc1580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +# 8.2.0 + +Add support for SSL (HTTPS) with `--ssl`, `--cert` and `--key` options. + +# 8.1.0 + +Add `--cors` flag to enable `Access-Control-Allow-Origin: *` + +# 8.0.4 + +Bump required deps. + +# 8.0.3 + +Fix `opts.live` as a string, allowing an array of options to be passed to filter file names. + +# 8.0.2 + +Fix flow so that bundling events start after server connects, also updated upstream in watchify-middleware. + +# 8.0.1 + +Fix parsing issue with LiveReload resp modifier. + # 8.0.0 The server code has been refactored to use connect/express-style middleware stacking. Fixes [#80](https://github.com/mattdesl/budo/issues/80), [#79](https://github.com/mattdesl/budo/issues/79), [#124](https://github.com/mattdesl/budo/issues/124), [#128](https://github.com/mattdesl/budo/issues/128). From b3c02a8ea6e507ce496d35d8bc8d38226920acc9 Mon Sep 17 00:00:00 2001 From: Ross Zurowski Date: Sun, 10 Apr 2016 23:39:58 -0400 Subject: [PATCH 292/302] add newline to end of 'help.txt' --- bin/help.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/help.txt b/bin/help.txt index 3e2f3c8..4ce938d 100644 --- a/bin/help.txt +++ b/bin/help.txt @@ -26,4 +26,4 @@ Options: --no-debug do not use inline source maps --no-portfind will not attempt auto-portfinding --no-error-handler disable default DOM error handling - --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' \ No newline at end of file + --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' From 44f26991c85434387e301174fd42105a4982d755 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 11 Apr 2016 20:36:07 +1000 Subject: [PATCH 293/302] 8.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa220a1..b59473c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.2.0", + "version": "8.2.1", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From a7dd8b3c3c955b899e503b05a5d3720cd506622b Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Apr 2016 09:41:32 -0400 Subject: [PATCH 294/302] add test for push state; fix serve option shorthand --- lib/parse-args.js | 1 + test/test-pushstate.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 test/test-pushstate.js diff --git a/lib/parse-args.js b/lib/parse-args.js index ec2b0bc..26de70e 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -30,6 +30,7 @@ function parseArgs (args, opt) { alias: { port: 'p', ssl: 'S', + serve: 's', cert: 'C', key: 'K', verbose: 'v', diff --git a/test/test-pushstate.js b/test/test-pushstate.js new file mode 100644 index 0000000..d93cf49 --- /dev/null +++ b/test/test-pushstate.js @@ -0,0 +1,22 @@ +var test = require('tape') +var budo = require('../') +var path = require('path') +var request = require('request') + +var file = path.join(__dirname, 'fixtures', 'app.js') +var html = 'budo' + +test('pushstate', function (t) { + t.plan(1) + var b = budo(file, { + port: 9966, + pushstate: true, + portfind: false + }).on('connect', function (ev) { + request({ uri: ev.uri + '/foobar' }, function (err, resp, body) { + b.close() + if (err) return t.fail(err) + t.equal(body, html, 'returns home index.html') + }) + }) +}) From ff8032542328cf4a5294c601c804bc3144c3eeb3 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 26 Apr 2016 09:41:37 -0400 Subject: [PATCH 295/302] 8.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b59473c..788cd47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.2.1", + "version": "8.2.2", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From 474298844974a37b606a9e209fc66709a3d0eb40 Mon Sep 17 00:00:00 2001 From: Gianluca Casati Date: Wed, 27 Apr 2016 12:56:38 +0200 Subject: [PATCH 296/302] fixed typo --- docs/api-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-usage.md b/docs/api-usage.md index 08b5ab4..adfa419 100644 --- a/docs/api-usage.md +++ b/docs/api-usage.md @@ -23,7 +23,7 @@ budo('./src/index.js', { browserify: { transform: babelify // use ES6 } -}).on('connnect', function(ev) { +}).on('connect', function(ev) { //... }) ``` From 38c1f15b8c1f696df167cbcd7070c357fc9dfd63 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 30 May 2016 15:12:48 -0400 Subject: [PATCH 297/302] bump to latest browserify --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 788cd47..55355f7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "bole": "^2.0.0", - "browserify": "^12.0.1", + "browserify": "^13.0.1", "chokidar": "^1.0.1", "connect-pushstate": "^1.0.0", "events": "^1.0.2", From c4aa94428b5abc064e4f9cd1dea4f3845921d570 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 30 May 2016 15:12:55 -0400 Subject: [PATCH 298/302] 8.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 55355f7..a0d5dde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "budo", - "version": "8.2.2", + "version": "8.3.0", "description": "a browserify server for rapid prototyping", "main": "index.js", "bin": { From a4a08a3cc2cb420c6465c28e9a2dffa6900518f1 Mon Sep 17 00:00:00 2001 From: Christian Talmo Date: Wed, 1 Jun 2016 19:25:56 +0300 Subject: [PATCH 299/302] added --proxy path@url command line argument (eg: --poryx /api@http://localhost:3000/api) --- README.md | 1 + lib/middleware.js | 16 ++++++++++++++++ lib/parse-args.js | 3 ++- package.json | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e5183bd..c1ec29d 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ Options: --no-portfind will not attempt auto-portfinding --no-error-handler disable default DOM error handling --watch-glob, --wg glob(s) to watch for reloads, default '**/*.{html,css}' + --proxy path@url proxy requests on given path to url (eg: --proxy /api@http://localhost:3000/api) ``` By default, messages will be printed to `process.stdout`, and `--debug` will be sent to browserify (for source maps). You can turn these off with `--no-stream` and `--no-debug`, respectively. diff --git a/lib/middleware.js b/lib/middleware.js index a6c00b5..cb74fcd 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -8,6 +8,9 @@ var pushState = require('connect-pushstate') var liveReload = require('inject-lr-script') var urlTrim = require('url-trim') +var argv = require('./parse-args.js')(process.argv.slice(2)) +var httpProxy = require('http-proxy') + module.exports = budoMiddleware function budoMiddleware (entryMiddleware, opts) { opts = opts || {} @@ -88,6 +91,19 @@ function budoMiddleware (entryMiddleware, opts) { // when none is found locally. handler.use(indexHandler) + // Attach proxy + if (argv.proxy) { + console.log('Attaching proxy at: ' + argv.proxy) + var split = argv.proxy.trim().split('@') + var proxy = httpProxy.createProxyServer({}); + handler.mount(split[0], function (req, res, next) { + console.log('-- proxying -- : ' + split[1] + req.url) + proxy.web(req, res, { target: split[1] + req.url }, function (err) { + if (err) next() + }) + }) + } + // Handle errors handler.use(function (req, res) { res.statusCode = 404 diff --git a/lib/parse-args.js b/lib/parse-args.js index 26de70e..ab6249c 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -24,7 +24,8 @@ function parseArgs (args, opt) { 'title', 'watchGlob', 'cert', - 'key' + 'key', + 'proxy' ], default: module.exports.defaults, alias: { diff --git a/package.json b/package.json index a0d5dde..8c74d0b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "events": "^1.0.2", "garnish": "^5.0.0", "get-ports": "^1.0.2", + "http-proxy": "^1.13.3", "inject-lr-script": "^2.0.0", "internal-ip": "^1.0.1", "micromatch": "^2.2.0", From 285b705137646c4ba6f77d1f5e827eb81853a614 Mon Sep 17 00:00:00 2001 From: Christian Talmo Date: Wed, 1 Jun 2016 19:27:01 +0300 Subject: [PATCH 300/302] removed semicolon --- lib/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/middleware.js b/lib/middleware.js index cb74fcd..a618594 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -95,7 +95,7 @@ function budoMiddleware (entryMiddleware, opts) { if (argv.proxy) { console.log('Attaching proxy at: ' + argv.proxy) var split = argv.proxy.trim().split('@') - var proxy = httpProxy.createProxyServer({}); + var proxy = httpProxy.createProxyServer({}) handler.mount(split[0], function (req, res, next) { console.log('-- proxying -- : ' + split[1] + req.url) proxy.web(req, res, { target: split[1] + req.url }, function (err) { From 4509fc3bb064c00bf1b88ff9fb9df280949d42b8 Mon Sep 17 00:00:00 2001 From: Christian Talmo Date: Fri, 3 Jun 2016 00:26:07 +0300 Subject: [PATCH 301/302] switched to using opts.proxy instead of argv.proxy, added --proxy tests at test/test-proxy.js, added vim tmp files to .gitignore --- .gitignore | 4 +++- lib/middleware.js | 8 +++----- test/test-proxy.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 test/test-proxy.js diff --git a/.gitignore b/.gitignore index 46180f5..2aa68ad 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ node_modules .DS_Store bundle.js bundle-expected.js -.tmp \ No newline at end of file +.tmp +*.swp +*.swo diff --git a/lib/middleware.js b/lib/middleware.js index a618594..c7bb44c 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -7,8 +7,6 @@ var urlLib = require('url') var pushState = require('connect-pushstate') var liveReload = require('inject-lr-script') var urlTrim = require('url-trim') - -var argv = require('./parse-args.js')(process.argv.slice(2)) var httpProxy = require('http-proxy') module.exports = budoMiddleware @@ -92,9 +90,9 @@ function budoMiddleware (entryMiddleware, opts) { handler.use(indexHandler) // Attach proxy - if (argv.proxy) { - console.log('Attaching proxy at: ' + argv.proxy) - var split = argv.proxy.trim().split('@') + if (opts.proxy) { + console.log('Attaching proxy at: ' + opts.proxy) + var split = opts.proxy.trim().split('@') var proxy = httpProxy.createProxyServer({}) handler.mount(split[0], function (req, res, next) { console.log('-- proxying -- : ' + split[1] + req.url) diff --git a/test/test-proxy.js b/test/test-proxy.js new file mode 100644 index 0000000..69552e3 --- /dev/null +++ b/test/test-proxy.js @@ -0,0 +1,50 @@ +var test = require('tape') +var budo = require('../') +var path = require('path') +var request = require('request') +var http = require('http') + +test('connect connect to proxy server api', function (t) { + t.plan(4) + t.timeoutAfter(6000) + + var server = http.createServer(function (req, res) { + // console.log('url: %s', req.url); + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.write(JSON.stringify({ message: 'hello world' })) + res.end() + }) + + server.on('close', function () { + t.ok(true, 'closing proxy server') + }) + + server.listen(9977, function () { + var entry = path.join(__dirname, 'fixtures', 'app.js') + var app = budo(entry, { + dir: __dirname, + port: 8000, + host: 'localhost', + serve: 'app.js', + live: true, + proxy: '/api@http://localhost:9977/api' + }) + .on('error', function (err) { + t.fail(err) + }) + .on('connect', function (ev) { + setTimeout(function () { + request('http://localhost:8000/api/hello', function (error, response, body) { + t.ok(!error, 'no proxy error') + var json = JSON.parse(body) + t.equal(json.message, 'hello world') + app.close() + server.close() + }) + }, 1000) + }) + .on('exit', function () { + t.ok(true, 'closing') + }) + }) +}) From 57300f4b5e05cfa01ff822d4b2cf3b044b775e70 Mon Sep 17 00:00:00 2001 From: Christian Talmo Date: Fri, 3 Jun 2016 00:47:14 +0300 Subject: [PATCH 302/302] fixed double path bug in proxy, improved tests, longer timeout --- lib/middleware.js | 2 +- test/test-proxy.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/middleware.js b/lib/middleware.js index c7bb44c..531ef66 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -96,7 +96,7 @@ function budoMiddleware (entryMiddleware, opts) { var proxy = httpProxy.createProxyServer({}) handler.mount(split[0], function (req, res, next) { console.log('-- proxying -- : ' + split[1] + req.url) - proxy.web(req, res, { target: split[1] + req.url }, function (err) { + proxy.web(req, res, { target: split[1] }, function (err) { if (err) next() }) }) diff --git a/test/test-proxy.js b/test/test-proxy.js index 69552e3..d326500 100644 --- a/test/test-proxy.js +++ b/test/test-proxy.js @@ -4,12 +4,13 @@ var path = require('path') var request = require('request') var http = require('http') -test('connect connect to proxy server api', function (t) { - t.plan(4) - t.timeoutAfter(6000) +test('connect to proxy server api', function (t) { + t.plan(6) + t.timeoutAfter(20000) var server = http.createServer(function (req, res) { - // console.log('url: %s', req.url); + // console.log('url: %s', req.url) + t.equal(req.url, '/api/hello') res.writeHead(200, { 'Content-Type': 'application/json' }) res.write(JSON.stringify({ message: 'hello world' })) res.end() @@ -19,15 +20,14 @@ test('connect connect to proxy server api', function (t) { t.ok(true, 'closing proxy server') }) - server.listen(9977, function () { + server.listen(3000, function () { var entry = path.join(__dirname, 'fixtures', 'app.js') var app = budo(entry, { dir: __dirname, port: 8000, host: 'localhost', serve: 'app.js', - live: true, - proxy: '/api@http://localhost:9977/api' + proxy: '/api@http://localhost:3000/api' }) .on('error', function (err) { t.fail(err) @@ -35,7 +35,9 @@ test('connect connect to proxy server api', function (t) { .on('connect', function (ev) { setTimeout(function () { request('http://localhost:8000/api/hello', function (error, response, body) { + if (error) t.fail(error) t.ok(!error, 'no proxy error') + t.ok(body, 'received proxy response body') var json = JSON.parse(body) t.equal(json.message, 'hello world') app.close()