Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass watchedNodes to Watcher/WatcherAdapter #403

Merged
merged 6 commits into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ module.exports = class Builder extends EventEmitter {
return nodeWrapper;
}

get watchedSourceNodeWrappers() {
return this.nodeWrappers.filter(nw => {
return nw.nodeInfo.nodeType === 'source' && nw.nodeInfo.watched;
});
}

checkInputPathsExist() {
// We might consider checking this.unwatchedPaths as well.
for (let i = 0; i < this.watchedPaths.length; i++) {
Expand Down
12 changes: 10 additions & 2 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ module.exports = function broccoliCLI(args) {
const builder = getBuilder(options);
const Watcher = getWatcher(options);
const outputDir = options.outputPath;
const watcher = new Watcher(builder, buildWatcherOptions(options));
const watcher = new Watcher(
builder,
builder.watchedSourceNodeWrappers,
buildWatcherOptions(options)
);

if (outputDir) {
try {
Expand Down Expand Up @@ -112,7 +116,11 @@ module.exports = function broccoliCLI(args) {
const builder = getBuilder(options);
const Watcher = getWatcher(options);
const outputTree = new TreeSync(builder.outputPath, outputDir);
const watcher = new Watcher(builder, buildWatcherOptions(options));
const watcher = new Watcher(
builder,
builder.watchedSourceNodeWrappers,
buildWatcherOptions(options)
);

watcher.on('buildSuccess', () => {
outputTree.sync();
Expand Down
10 changes: 3 additions & 7 deletions lib/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ const logger = require('heimdalljs-logger')('broccoli:watcher');
// principle.

module.exports = class Watcher extends EventEmitter {
constructor(builder, options) {
constructor(builder, watchedNodes, options) {
super();
this.options = options || {};
if (this.options.debounce == null) {
this.options.debounce = 100;
}
this.builder = builder;
this.watcherAdapter =
this.options.watcherAdapter || new WatcherAdapter(this.options.saneOptions);
this.options.watcherAdapter || new WatcherAdapter(watchedNodes, this.options.saneOptions);
this.currentBuild = null;
this._rebuildScheduled = false;
this._ready = false;
Expand All @@ -41,11 +41,7 @@ module.exports = class Watcher extends EventEmitter {
this.watcherAdapter.on('error', this._error.bind(this));
this.currentBuild = Promise.resolve()
.then(() => {
let watchedSourceNodes = this.builder.nodeWrappers.filter(nw => {
return nw.nodeInfo.nodeType === 'source' && nw.nodeInfo.watched;
});

return this.watcherAdapter.watch(watchedSourceNodes);
return this.watcherAdapter.watch();
})
.then(() => {
logger.debug('ready');
Expand Down
26 changes: 18 additions & 8 deletions lib/watcher_adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const EventEmitter = require('events').EventEmitter;
const sane = require('sane');
const logger = require('heimdalljs-logger')('broccoli:watcherAdapter');
const SourceNode = require('./wrappers/source-node');

function defaultFilterFunction(name) {
return /^[^.]/.test(name);
Expand All @@ -18,20 +19,29 @@ function bindFileEvent(adapter, watcher, node, event) {
}

module.exports = class WatcherAdapter extends EventEmitter {
constructor(options) {
constructor(watchedNodes, options) {
super();
if (!Array.isArray(watchedNodes)) {
throw new TypeError(
`WatcherAdapter's first argument must be an array of SourceNodeWrapper nodes`
);
}
for (const node of watchedNodes) {
if (!(node instanceof SourceNode)) {
throw new Error(`${node} is not a SourceNode`);
}
if (node.nodeInfo.watched !== true) {
throw new Error(`${node.nodeInfo.sourceDirectory} it not watched`);
}
}
this.watchedNodes = watchedNodes;
this.options = options || {};
this.options.filter = this.options.filter || defaultFilterFunction;
this.watchers = [];
}

watch(watchedSourceNodes) {
if (!Array.isArray(watchedSourceNodes)) {
throw new TypeError(
`WatcherAdapter#watch's first argument must be an array of WatchedDir nodes`
);
}
let watchers = watchedSourceNodes.map(node => {
watch() {
let watchers = this.watchedNodes.map(node => {
const watchedPath = node.nodeInfo.sourceDirectory;
const watcher = new sane(watchedPath, this.options);
this.watchers.push(watcher);
Expand Down
11 changes: 11 additions & 0 deletions test/builder_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,17 @@ describe('Builder', function() {
/test\/fixtures\/basic\/foo\.txt: Not a directory/
);
});

it('returns only watched SourceNodeWrappers from watchedSourceNodeWrappers', function() {
const source1 = new broccoliSource.WatchedDir('test/fixtures/basic/');
const source2 = new broccoliSource.UnwatchedDir('test/fixtures/empty/');
builder = new Builder(new plugins.Merge([source1, source2]));

const watchedSourceNodeWrappers = builder.watchedSourceNodeWrappers;

expect(watchedSourceNodeWrappers).length.to.be(1);
expect(watchedSourceNodeWrappers[0].node).to.equal(source1);
});
});

describe('error handling in constructor', function() {
Expand Down
14 changes: 11 additions & 3 deletions test/cli_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'build', 'dist', '--watch']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand Down Expand Up @@ -140,6 +141,7 @@ describe('cli', function() {
() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand All @@ -159,6 +161,7 @@ describe('cli', function() {
() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand All @@ -176,6 +179,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'build', 'dist', '--watch', '--watcher', 'node']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand Down Expand Up @@ -332,6 +336,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'serve']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand Down Expand Up @@ -541,6 +546,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'serve', '--watcher', 'watchman']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand All @@ -559,6 +565,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'serve', '--watcher', 'node']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand All @@ -577,6 +584,7 @@ describe('cli', function() {
return cli(['node', 'broccoli', 'serve', '--watcher', 'polling']).then(() =>
chai.expect(spy).to.have.been.calledWith(
sinon.match.instanceOf(Builder),
sinon.match.array,
sinon.match.has(
'saneOptions',
sinon.match({
Expand All @@ -595,9 +603,9 @@ function createWatcherSpy() {
const spy = sinon.spy();
sinon.stub(broccoli, 'Watcher').value(
class Watcher extends DummyWatcher {
constructor(builder, options) {
super(builder, options);
spy.call(null, builder, options);
constructor(builder, watchedSourceNodes, options) {
super(builder, watchedSourceNodes, options);
spy.call(null, builder, watchedSourceNodes, options);
}

start() {
Expand Down
105 changes: 65 additions & 40 deletions test/watcher_adapter_test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

const WatcherAdapter = require('../lib/watcher_adapter');
const TransformNodeWrapper = require('../lib/wrappers/transform-node');
const SourceNodeWrapper = require('../lib/wrappers/source-node');
const bindFileEvent = WatcherAdapter.bindFileEvent;
const fs = require('fs');
const chai = require('chai');
Expand All @@ -14,6 +16,29 @@ describe('WatcherAdapter', function() {
sinon.restore();
});

const FIXTURE_BASIC = __dirname + '/fixtures/basic';
const FIXTURE_PROJECT = __dirname + '/fixtures/project';
const watchedNodeBasic = new SourceNodeWrapper();
watchedNodeBasic.nodeInfo = {
nodeType: 'source',
sourceDirectory: FIXTURE_BASIC,
watched: true,
};

const unwatchedNodeBasic = new SourceNodeWrapper();
unwatchedNodeBasic.nodeInfo = {
nodeType: 'source',
sourceDirectory: FIXTURE_BASIC,
watched: false,
};

const watchedNodeProject = new SourceNodeWrapper();
watchedNodeProject.nodeInfo = {
nodeType: 'source',
sourceDirectory: FIXTURE_PROJECT,
watched: true,
};

describe('bindFileEvent', function() {
const adapter = {
emit() {},
Expand Down Expand Up @@ -71,15 +96,15 @@ describe('WatcherAdapter', function() {
});

it('has defaults', function() {
const adapter = new WatcherAdapter();
const adapter = new WatcherAdapter([]);

expect(adapter.options).to.have.keys('filter');
expect(adapter.options.filter).to.have.be.a('Function');
});

it('supports custom options, but without filter', function() {
const customOptions = {};
const adapter = new WatcherAdapter(customOptions);
const adapter = new WatcherAdapter([], customOptions);

expect(adapter.options).to.eql(customOptions);
expect(adapter.options.filter).to.have.be.a('Function');
Expand All @@ -88,11 +113,45 @@ describe('WatcherAdapter', function() {
it('supports custom options, and allows for a custom filter', function() {
function filter() {}
const customOptions = { filter };
const adapter = new WatcherAdapter(customOptions);
const adapter = new WatcherAdapter([], customOptions);

expect(adapter.options).to.eql(customOptions);
expect(adapter.options.filter).to.eql(filter);
});

it('throws if you try to watch a non array', function() {
expect(() => new WatcherAdapter()).to.throw(
TypeError,
`WatcherAdapter's first argument must be an array of SourceNodeWrapper nodes`
);

[null, undefined, NaN, {}, { length: 0 }, 'string', function() {}, Symbol('OMG')].forEach(
arg => {
expect(() => new WatcherAdapter(arg)).to.throw(
TypeError,
`WatcherAdapter's first argument must be an array of SourceNodeWrapper nodes`
);
}
);
});

it('throws if you try to watch a non node', function() {
expect(() => new WatcherAdapter([{}])).to.throw(Error, `[object Object] is not a SourceNode`);
});

it('throws if you try to watch a non-source node', function() {
expect(() => new WatcherAdapter([new TransformNodeWrapper()])).to.throw(
Error,
`[NodeWrapper:undefined undefined at undefined] is not a SourceNode`
);
});

it('throws if you try to watch a non-watchable node', function() {
expect(() => new WatcherAdapter([unwatchedNodeBasic])).to.throw(
Error,
`/Users/goli/Projects/broccoli/test/fixtures/basic it not watched`
stefanpenner marked this conversation as resolved.
Show resolved Hide resolved
);
});
});

describe('watch', function() {
Expand All @@ -103,58 +162,24 @@ describe('WatcherAdapter', function() {
const FIXTURE_PROJECT = __dirname + (isWin ? '\\fixtures\\project' : '/fixtures/project');
let adapter;

const watchedNodeBasic = {
revise() {},
nodeInfo: {
nodeType: 'source',
sourceDirectory: FIXTURE_BASIC,
},
};

const watchedNodeProject = {
revise() {},
nodeInfo: {
nodeType: 'source',
sourceDirectory: FIXTURE_PROJECT,
},
};

afterEach(function() {
adapter.quit();
});

it('supports symmetric start/shutdown', function() {
adapter = new WatcherAdapter();
});

it('throws if you try to watch a non array', function() {
adapter = new WatcherAdapter();

expect(() => adapter.watch()).to.throw(
TypeError,
`WatcherAdapter#watch's first argument must be an array of WatchedDir nodes`
);

[null, undefined, NaN, {}, { length: 0 }, 'string', function() {}, Symbol('OMG')].forEach(
arg => {
expect(() => adapter.watch(arg)).to.throw(
TypeError,
`WatcherAdapter#watch's first argument must be an array of WatchedDir nodes`
);
}
);
adapter = new WatcherAdapter([]);
});

it('actually works !!', function() {
adapter = new WatcherAdapter();
adapter = new WatcherAdapter([watchedNodeBasic]);
const changeHandler = sinon.spy();
adapter.on('change', changeHandler);

expect(changeHandler).to.have.callCount(0);

expect(adapter.watchers.length).to.eql(0);

let watching = adapter.watch([watchedNodeBasic]);
let watching = adapter.watch();

expect(adapter.watchers.length).to.eql(1);

Expand Down
Loading