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

broccoli

Package Overview
Dependencies
Maintainers
1
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

broccoli - npm Package Compare versions

Comparing version 1.0.0-beta.1 to 1.0.0-beta.2

10

CHANGELOG.md
# master
# 1.0.0-beta.2
* Add `watcher.watch()` method. `Watcher` no longer automatically starts
watching; instead, you must call this method explicitly. It returns a promise
that is fulfilled if you later call `watcher.quit()`, or rejected if watching
one of the source directories fails.
- `server` will call `watcher.watch()` for you.
- In contrast, `getMiddleware` expects a watcher that is already watching.
# 1.0.0-beta.1

@@ -4,0 +14,0 @@

2

lib/middleware.js

@@ -11,2 +11,4 @@ var path = require('path')

// You must call watcher.watch() before you call `getMiddleware`
//
// Supported options:

@@ -13,0 +15,0 @@ // autoIndex (default: true) - set to false to disable directory listings

29

lib/server.js

@@ -14,3 +14,3 @@ var Watcher = require('./watcher')

server.watcher = options.watcher || new Watcher(builder, {verbose: true})
server.watcher = options.watcher || new Watcher(builder)

@@ -21,14 +21,21 @@ server.app = connect().use(middleware(server.watcher))

server.watcher.watch()
.catch(function(err) {
console.log(err && err.stack || err)
})
.finally(function() {
builder.cleanup()
server.http.close()
})
.catch(function(err) {
console.log('Cleanup error:')
console.log(err && err.stack || err)
})
.finally(function() {
process.exit(1)
})
// We register these so the 'exit' handler removing temp dirs is called
function cleanupAndExit() {
return server.watcher.quit()
.then(function() {
builder.cleanup()
})
.catch(function(err) {
console.error('Cleanup error:')
console.error(err && err.stack ? err.stack : err)
}).finally(function() {
process.exit(1)
})
}

@@ -46,3 +53,3 @@

console.log('Built with error:')
console.error(err.message)
console.log(err.message)
if (!err.broccoliPayload || !err.broccoliPayload.location.file) {

@@ -49,0 +56,0 @@ console.log('')

@@ -15,4 +15,3 @@ 'use strict'

this.initialBuildStarted = false
this.check()
this.watchDeferred = null
}

@@ -22,2 +21,11 @@

Watcher.prototype.watch = function() {
var self = this
if (this.watchDeferred != null) throw new Error('watcher.watch() must only be called once')
this.watchDeferred = RSVP.defer()
self.check()
return this.watchDeferred.promise
}
Watcher.prototype.detectChanges = function () {

@@ -41,8 +49,10 @@ var changedPaths = []

this.timeoutID = null
// .check can be scheduled via setTimeout or via .then, so we cannot just
// rely on clearTimeout
if (this.quitting) return
// .check can be scheduled via setTimeout or via .then, so we cannot
// just rely on clearTimeout for quitting
if (this.quitting) {
return
}
try {
var interval = this.options.interval || 100
var changedPaths = this.detectChanges()

@@ -62,2 +72,3 @@

self.trigger('error', err)
// Do not rethrow
})

@@ -68,15 +79,14 @@ .then(function() {

}, function(err) {
// Errors here are due to errors in change/error event handlers
console.error('An unexpected error occurred. Watcher quitting.')
console.error(err.stack)
// Rethrow in case a debugging tool wants to catch it
throw err
// A 'change' or 'error' event handler threw an error
self.watchDeferred.reject(err)
})
} else {
// Schedule next check in 100 milliseconds
var interval = this.options.interval || 100
this.timeoutID = setTimeout(this.check.bind(this), interval)
}
} catch (err) {
console.error('Uncaught error in Broccoli file watcher:')
console.error(err.stack)
console.error('Watcher quitting') // do not schedule check with setTimeout
// An error occurred in this.detectChanges(); this is usually because one
// of the watched source directories is missing
this.watchDeferred.reject(err)
}

@@ -87,2 +97,4 @@ }

Watcher.prototype.quit = function() {
var self = this
this.quitting = true

@@ -93,3 +105,13 @@ if (this.timeoutID) {

}
return this.currentBuild.catch(function() { /* always fulfill, never reject */ })
return RSVP.resolve(this.currentBuild)
.catch(function(err) {
// Ignore build errors to stop them from being propagated to
// RSVP.on('error')
})
.finally(function() {
// It might have been rejected in the meantime, in which case this has
// no effect
self.watchDeferred.resolve()
})
}
{
"name": "broccoli",
"description": "Fast client-side asset builder",
"version": "1.0.0-beta.1",
"version": "1.0.0-beta.2",
"author": "Jo Liss <joliss42@gmail.com>",

@@ -22,3 +22,3 @@ "main": "lib/index.js",

"dependencies": {
"broccoli-kitchen-sink-helpers": "^0.2.9",
"broccoli-kitchen-sink-helpers": "^0.3.0",
"broccoli-slow-trees": "^2.0.0",

@@ -25,0 +25,0 @@ "broccoli-source": "^1.1.0",

@@ -17,9 +17,17 @@ 'use strict'

// Pass in the Watcher class you wish to test, as well as broccoli.Builder and
// a sleepDuration in milliseconds. Your Watcher class should reliably pick up
// changes within `sleepDuration` milliseconds.
// Parameters:
//
// Watcher: The Watcher class you wish to test
// Builder: The broccoli.Builder class
// sleepDuration: Your Watcher class should reliably pick up
// changes to the file system within `sleepDuration` milliseconds.
// tmpBaseDir: A watcher_test.tmp directory will be created and deleted
// underneath this directory; e.g. if you pass 'test', the directory
// will be test/watcher_test.tmp
//
// Requires mocha for describe/it syntax.
module.exports = function(Watcher, Builder, sleepDuration) {
module.exports = function(Watcher, Builder, sleepDuration, tmpBaseDir) {
var tmpDir = tmpBaseDir + '/watcher_test.tmp'
function sleep() {

@@ -32,7 +40,7 @@ return new RSVP.Promise(function(resolve, reject) {

describe('Watcher', function() {
var builder, buildSpy, watcher
var builder, buildSpy, watcher, watchPromise
beforeEach(function() {
rimraf.sync('test/tmp')
fs.mkdirSync('test/tmp')
rimraf.sync(tmpDir)
fs.mkdirSync(tmpDir)
})

@@ -44,8 +52,7 @@

if (watcher) {
var promise = watcher.quit()
watcher = null
return promise.catch(function(err) {})
return watcher.quit()
}
})
.then(function() {
watcher = null
if (builder) {

@@ -56,3 +63,3 @@ builder.cleanup()

buildSpy = null
rimraf.sync('test/tmp')
rimraf.sync(tmpDir)
})

@@ -62,9 +69,9 @@ })

function makeNodeWithTwoWatchedDirectories() {
fs.mkdirSync('test/tmp/1')
fs.mkdirSync('test/tmp/1/subdir')
fs.writeFileSync('test/tmp/1/subdir/foo', 'x')
fs.mkdirSync('test/tmp/2')
fs.mkdirSync(tmpDir + '/1')
fs.mkdirSync(tmpDir + '/1/subdir')
fs.writeFileSync(tmpDir + '/1/subdir/foo', 'x')
fs.mkdirSync(tmpDir + '/2')
return new plugins.NoopPlugin([
new WatchedDir('test/tmp/1'),
new WatchedDir('test/tmp/2')
new WatchedDir(tmpDir + '/1'),
new WatchedDir(tmpDir + '/2')
])

@@ -77,2 +84,3 @@ }

watcher = new Watcher(builder)
watchPromise = watcher.watch()
}

@@ -105,3 +113,3 @@

return expect(triggersRebuild(function() {
fs.writeFileSync('test/tmp/1/subdir/bar', 'hello')
fs.writeFileSync(tmpDir + '/1/subdir/bar', 'hello')
})).to.be.eventually.true

@@ -113,3 +121,3 @@ })

return expect(triggersRebuild(function() {
fs.unlinkSync('test/tmp/1/subdir/foo')
fs.unlinkSync(tmpDir + '/1/subdir/foo')
})).to.be.eventually.true

@@ -121,3 +129,3 @@ })

return expect(triggersRebuild(function() {
fs.writeFileSync('test/tmp/1/subdir/foo', 'y')
fs.writeFileSync(tmpDir + '/1/subdir/foo', 'y')
})).to.be.eventually.true

@@ -129,3 +137,3 @@ })

return expect(triggersRebuild(function() {
fs.mkdirSync('test/tmp/1/another-subdir')
fs.mkdirSync(tmpDir + '/1/another-subdir')
})).to.be.eventually.true

@@ -135,4 +143,4 @@ })

describe('Watcher.currentBuild', function() {
it ('is fulfilled when the build succeeds', function() {
describe('watcher.currentBuild', function() {
it('is fulfilled when the build succeeds', function() {
setUpBuilderAndWatcher(new plugins.NoopPlugin)

@@ -142,3 +150,3 @@ return expect(watcher.currentBuild).to.be.fulfilled

it ('is rejected when the build fails', function() {
it('is rejected when the build fails', function() {
setUpBuilderAndWatcher(new plugins.FailingPlugin(new Error('fail me')))

@@ -204,12 +212,31 @@ return expect(watcher.currentBuild).to.be.rejected

describe('watcher.watch() promise', function() {
it('is fulfilled when watcher.quit() is called', function() {
setUpBuilderAndWatcher(new plugins.FailingPlugin) // even if build fails
watcher.quit()
return expect(watchPromise).to.be.fulfilled
})
// We could relax this in the future and turn missing source directories
// into transient build errors, by always watching the parent
// directories
it('is rejected when a watched source directory does not exist', function() {
setUpBuilderAndWatcher(new WatchedDir('doesnotexist'))
return expect(watchPromise).to.be.rejected
})
})
describe('watcher.quit()', function() {
it('stops watching', function() {
it('if no build is in progress, just stops watching and fulfills watch() promise', function() {
setUpBuilderAndWatcher(makeNodeWithTwoWatchedDirectories())
return watcher.currentBuild
.then(function() {
expect(buildSpy).to.have.been.calledOnce
return watcher.quit()
var quitPromise = watcher.quit()
// Must be a promise (that presumably fulfills immediately), even
// though no build is in progress
expect(quitPromise.then).to.be.a('function')
return quitPromise
})
.then(function() {
fs.writeFileSync('test/tmp/1/subdir/bar', 'hello')
fs.writeFileSync(tmpDir + '/1/subdir/bar', 'hello')
})

@@ -222,4 +249,4 @@ .then(sleep)

it('returns a promise until the current rebuild has finished', function() {
var node = new plugins.AsyncPlugin
it('if a build is in progress, returns a promise until it finishes, then stops watching', function() {
var node = new plugins.AsyncPlugin([makeNodeWithTwoWatchedDirectories()])
setUpBuilderAndWatcher(node)

@@ -232,2 +259,3 @@

.then(function() {
// Quit while node is being built
quitPromise = watcher.quit().then(function() {

@@ -239,2 +267,3 @@ quitPromiseHasBeenFulfilled = true

.then(function() {
// .quit() promise should not be fulfilled until build has finished
expect(quitPromiseHasBeenFulfilled).to.be.false

@@ -244,2 +273,11 @@ node.finishBuild()

})
.then(function() {
fs.writeFileSync(tmpDir + '/1/subdir/bar', 'hello')
})
.then(sleep)
.then(function() {
// No further rebuilds should happen, even if files change
expect(buildSpy).to.have.been.calledOnce
})
})

@@ -246,0 +284,0 @@

@@ -13,2 +13,2 @@ 'use strict'

require('./watcher_test_suite')(FastWatcher, Builder, 15)
require('./watcher_test_suite')(FastWatcher, Builder, 15, 'test')
SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc