Skip to content

Commit

Permalink
Merge pull request #319 from dwelch2344/watch-compile-hook
Browse files Browse the repository at this point in the history
#315 - Add watch-compile event
  • Loading branch information
HyperBrain authored Feb 9, 2018
2 parents 34dc85c + 3524849 commit 66cb262
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ All events (H) can be hooked by a plugin.
-> webpack:validate:validate (H)
-> webpack:compile
-> webpack:compile:compile (H)
-> webpack:compile:watch:compile (H)
-> webpack:package
-> webpack:package:packExternalModules (H)
-> webpack:package:packageModules (H)
Expand Down
10 changes: 10 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ class ServerlessWebpack {
lifecycleEvents: [
'compile',
],
commands: {
watch: {
type: 'entrypoint',
lifecycleEvents: [
'compile'
]
}
}
},
package: {
type: 'entrypoint',
Expand Down Expand Up @@ -139,6 +147,8 @@ class ServerlessWebpack {
'webpack:compile:compile': () => BbPromise.bind(this)
.then(this.compile),

'webpack:compile:watch:compile': () => BbPromise.resolve(),

'webpack:package:packExternalModules': () => BbPromise.bind(this)
.then(this.packExternalModules),

Expand Down
32 changes: 29 additions & 3 deletions lib/wpwatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ module.exports = {
};

// This starts the watch and waits for the immediate compile that follows to end or fail.
let lastHash = null;
const startWatch = (callback) => {
let firstRun = true;
compiler.watch(watchOptions, (err, stats) => {
const watcher = compiler.watch(watchOptions, (err, stats) => {
if (err) {
if (firstRun) {
firstRun = false;
Expand All @@ -42,15 +43,40 @@ module.exports = {
throw err;
}

process.env.SLS_DEBUG && this.serverless.cli.log(`Webpack watch invoke: HASH NEW=${stats.hash} CUR=${lastHash}`);

// If the file hash did not change there were no effective code changes detected
// (comment changes do not change the compile hash and do not account for a rebuild!)
// See here: https://webpack.js.org/api/node/#watching (note below watching)
if (stats && stats.hash === lastHash) {
if (firstRun) {
firstRun = false;
callback();
}
return;
}

if (stats) {
lastHash = stats.hash;
this.serverless.cli.consoleLog(stats.toString(consoleStats));
}

this.serverless.cli.log('Watching for changes...');

if (firstRun) {
firstRun = false;
this.serverless.cli.log('Watching for changes...');
callback();
} else {
// We will close the watcher while the compile event is triggered and resume afterwards to prevent race conditions.
watcher.close(() => {
return this.serverless.pluginManager.spawn('webpack:compile:watch')
.then(() => {
// Resume watching after we triggered the compile:watch event
return BbPromise.fromCallback(cb => {
startWatch(cb);
})
.then(() => this.serverless.cli.log('Watching for changes...'));
});
});
}
});
};
Expand Down
23 changes: 23 additions & 0 deletions tests/run.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,29 @@ describe('run', () => {
expect(module.isWatching).to.be.true;
});

it('should not spawn on watch first run', () => {
module.isWatching = false;
const watch = module.watch.bind(module);
webpackMock.compilerMock.watch = sandbox.stub().yields(null, {});
_.set(module, 'options.function', 'myFunction');

watch('compile:watch:compile');
expect(spawnStub).to.not.have.been.called;
expect(module.isWatching).to.be.true;
});

it('should spawn on watch second run', () => {
module.isWatching = false;
const watch = module.watch.bind(module);
webpackMock.compilerMock.watch = sandbox.stub().yields(null, {});
_.set(module, 'options.function', 'myFunction');

watch('compile:watch:compile');
watch('compile:watch:compile');
expect(spawnStub).to.have.been.calledOnce;
expect(module.isWatching).to.be.true;
});

it('should spawn invoke local on subsequent runs', () => {
module.isWatching = true;
const watch = module.watch.bind(module);
Expand Down
12 changes: 9 additions & 3 deletions tests/webpack.mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ const StatsMock = () => ({
toString: sinon.stub().returns('testStats'),
});

const CompilerMock = (sandbox, statsMock) => ({
const WatchMock = sandbox => ({
close: sandbox.stub().callsFake(cb => cb())
});

const CompilerMock = (sandbox, statsMock, watchMock) => ({
run: sandbox.stub().yields(null, statsMock),
watch: sandbox.stub().yields(null, statsMock)
watch: sandbox.stub().returns(watchMock).yields(null, statsMock)
});

const webpackMock = sandbox => {
const statsMock = StatsMock(sandbox);
const compilerMock = CompilerMock(sandbox, statsMock);
const watchMock = WatchMock(sandbox);
const compilerMock = CompilerMock(sandbox, statsMock, watchMock);
const mock = sinon.stub().returns(compilerMock);
mock.compilerMock = compilerMock;
mock.statsMock = statsMock;
mock.watchMock = watchMock;
return mock;
};

Expand Down
15 changes: 9 additions & 6 deletions tests/wpwatch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,26 @@ describe('wpwatch', function() {
it('should call callback on subsequent runs', () => {
const wpwatch = module.wpwatch.bind(module);
let watchCallbackSpy;
webpackMock.compilerMock.watch.callsFake((options, cb) => {
webpackMock.compilerMock.watch.onFirstCall().callsFake((options, cb) => {
// We'll spy the callback registered for watch
watchCallbackSpy = sandbox.spy(cb);

// Schedule second call after 2 seconds
setTimeout(() => {
watchCallbackSpy(null, { call: 2 });
process.nextTick(() => watchCallbackSpy(null, { call: 2, hash: '2' }));
}, 2000);
process.nextTick(() => watchCallbackSpy(null, { call: 1 }));
process.nextTick(() => watchCallbackSpy(null, { call: 1, hash: '1' }));
return webpackMock.watchMock;
});
spawnStub.resolves();

return expect(wpwatch()).to.be.fulfilled
.then(() => BbPromise.delay(3000))
.then(() => BbPromise.join(
expect(spawnStub).to.not.have.been.called,
expect(webpackMock.compilerMock.watch).to.have.been.calledOnce,
expect(spawnStub).to.have.been.calledOnce,
expect(spawnStub).to.have.been.calledWithExactly('webpack:compile:watch'),
expect(webpackMock.compilerMock.watch).to.have.been.calledTwice,
expect(webpackMock.watchMock.close).to.have.been.calledOnce,
expect(watchCallbackSpy).to.have.been.calledTwice
));
});
Expand All @@ -181,7 +184,7 @@ describe('wpwatch', function() {
// Ignore the exception. The spy will record it.
}
}, 2000);
process.nextTick(() => watchCallbackSpy(null, { call: 1 }));
process.nextTick(() => watchCallbackSpy(null, { call: 3, hash: '3' }));
});
spawnStub.resolves();

Expand Down

0 comments on commit 66cb262

Please sign in to comment.