Skip to content

Commit d2f9978

Browse files
committed
Only start app once, call fastboot.reload
Remove `npm install` from watch cycle As @ef4 pointed out, it's not required, as the node_modules in the root of the project will be used
1 parent 2ade9ef commit d2f9978

3 files changed

Lines changed: 79 additions & 171 deletions

File tree

index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ module.exports = {
139139
return fastbootBuild.toTree();
140140
},
141141

142+
outputReady: function() {
143+
this.emit('outputReady');
144+
},
145+
142146
postBuild: function() {
143147
this.emit('postBuild');
144148
},
@@ -154,5 +158,4 @@ module.exports = {
154158

155159
return checker.for('ember', 'bower');
156160
},
157-
158161
};

lib/tasks/fastboot-server.js

Lines changed: 60 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = CoreObject.extend({
1313
exec,
1414
http,
1515
httpServer: null,
16+
fastboot: null,
1617
nextSocketId: 0,
1718
require,
1819
restartAgain: false,
@@ -21,54 +22,61 @@ module.exports = CoreObject.extend({
2122

2223
run(options) {
2324
debug('run');
24-
const restart = () => this.restart(options);
25-
this.addon.on('postBuild', restart);
25+
const ready = () => this.outputReady(options);
26+
this.addon.on('outputReady', ready);
2627
},
2728

2829
start(options) {
2930
debug('start');
30-
this.ui.writeLine('Installing FastBoot npm dependencies');
31-
32-
return this.exec('npm install', { cwd: options.outputPath })
33-
.then(() => {
34-
const middleware = this.require('fastboot-express-middleware')(options.outputPath);
35-
const express = this.require('express');
36-
const app = express();
37-
app.use(require('compression')());
38-
39-
if (options.serveAssets) {
40-
app.get('/', middleware);
41-
app.use(express.static(options.assetsPath));
42-
}
43-
app.get('/*', middleware);
44-
app.use((req, res) => res.sendStatus(404));
45-
46-
this.httpServer = this.http.createServer(app);
47-
48-
// Track open sockets for fast restart
49-
this.httpServer.on('connection', (socket) => {
50-
const socketId = this.nextSocketId++;
51-
debug(`open socket ${socketId}`);
52-
this.sockets[socketId] = socket;
53-
socket.on('close', () => {
54-
debug(`close socket ${socketId}`);
55-
delete this.sockets[socketId];
56-
});
57-
});
58-
59-
return new RSVP.Promise((resolve, reject) => {
60-
this.httpServer.listen(options.port, options.host, (err) => {
61-
if (err) { return reject(err); }
62-
const o = this.httpServer.address();
63-
const port = o.port;
64-
const family = o.family;
65-
let host = o.address;
66-
if (family === 'IPv6') { host = `[${host}]`; }
67-
this.ui.writeLine(`Ember FastBoot running at http://${host}:${port}`);
68-
resolve();
69-
});
70-
});
31+
32+
const fastbootMiddleware = this.require('fastboot-express-middleware');
33+
const FastBoot = this.require('fastboot');
34+
const express = this.require('express');
35+
36+
this.fastboot = new FastBoot({
37+
distPath: options.outputPath
38+
});
39+
40+
const middleware = fastbootMiddleware({
41+
outputPath: options.outputPath,
42+
fastboot: this.fastboot
43+
});
44+
45+
const app = express();
46+
app.use(require('compression')());
47+
48+
if (options.serveAssets) {
49+
app.get('/', middleware);
50+
app.use(express.static(options.assetsPath));
51+
}
52+
app.get('/*', middleware);
53+
app.use((req, res) => res.sendStatus(404));
54+
55+
this.httpServer = this.http.createServer(app);
56+
57+
// Track open sockets for fast restart
58+
this.httpServer.on('connection', (socket) => {
59+
const socketId = this.nextSocketId++;
60+
debug(`open socket ${socketId}`);
61+
this.sockets[socketId] = socket;
62+
socket.on('close', () => {
63+
debug(`close socket ${socketId}`);
64+
delete this.sockets[socketId];
7165
});
66+
});
67+
68+
return new RSVP.Promise((resolve, reject) => {
69+
this.httpServer.listen(options.port, options.host, (err) => {
70+
if (err) { return reject(err); }
71+
const o = this.httpServer.address();
72+
const port = o.port;
73+
const family = o.family;
74+
let host = o.address;
75+
if (family === 'IPv6') { host = `[${host}]`; }
76+
this.ui.writeLine(`Ember FastBoot running at http://${host}:${port}`);
77+
resolve();
78+
});
79+
});
7280
},
7381

7482
stop() {
@@ -89,39 +97,18 @@ module.exports = CoreObject.extend({
8997
});
9098
},
9199

92-
restart(options) {
93-
if (this.restartPromise) {
94-
debug('schedule immediate restart');
95-
this.restartAgain = true;
96-
return;
97-
}
98-
debug('restart');
99-
this.restartPromise = this.stop()
100-
.then(() => this.clearRequireCache(options.outputPath))
101-
.then(() => this.start(options))
102-
.catch(e => this.printError(e))
103-
.finally(() => {
104-
this.restartPromise = null;
105-
if (this.restartAgain) {
106-
debug('restart again');
107-
this.restartAgain = false;
108-
this.restart(options);
109-
}
110-
});
111-
return this.restartPromise;
100+
restart() {
101+
return this.fastboot.reload();
112102
},
113103

114-
clearRequireCache: function (serverRoot) {
115-
debug('clearRequireCache');
116-
let absoluteServerRoot = path.resolve(serverRoot);
117-
if (absoluteServerRoot.slice(-1) !== path.sep) {
118-
absoluteServerRoot += path.sep;
104+
outputReady(options) {
105+
if (this.fastboot) {
106+
this.ui.writeLine(`Reloading FastBoot`);
107+
return this.restart();
108+
} else {
109+
return this.start(options)
110+
.catch(e => this.printError(e));
119111
}
120-
Object.keys(require.cache).forEach(function (key) {
121-
if (key.indexOf(absoluteServerRoot) === 0) {
122-
delete require.cache[key];
123-
}
124-
});
125112
},
126113

127114
/*

test/lib-tasks-fastboot-server-test.js

Lines changed: 15 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -54,101 +54,33 @@ describe('fastboot server task', function() {
5454
});
5555

5656
describe('run', function() {
57-
it('calls restart on postBuild', function() {
58-
const restartStub = this.sinon.stub(task, 'restart');
57+
it('calls restart on outputReady', function() {
58+
const restartStub = this.sinon.stub(task, 'outputReady', function() {});
5959
task.run(options);
60-
addon.emit('postBuild');
60+
addon.emit('outputReady');
6161
expect(restartStub.called).to.be.ok;
6262
});
6363
});
6464

6565
describe('restart', function() {
66-
let restartSpy, stopStub, clearRequireCacheStub, startStub;
66+
let fbReloadStub, oldFb;
6767

6868
beforeEach(function() {
69-
restartSpy = this.sinon.spy(task, 'restart');
70-
stopStub = this.sinon.stub(task, 'stop').returns(RSVP.resolve());
71-
clearRequireCacheStub = this.sinon.stub(task, 'clearRequireCache');
72-
startStub = this.sinon.stub(task, 'start');
69+
oldFb = task.fastboot;
70+
fbReloadStub = this.sinon.stub().returns(RSVP.resolve());
71+
task.fastboot = {
72+
reload: fbReloadStub
73+
};
7374
});
7475

75-
it('calls stop, clearRequireCache, and start', function() {
76-
return task.restart(options).then(() => {
77-
expect(restartSpy.callCount).to.equal(1);
78-
expect(stopStub.callCount).to.equal(1);
79-
expect(clearRequireCacheStub.callCount).to.equal(1);
80-
expect(startStub.callCount).to.equal(1);
81-
});
76+
afterEach(function() {
77+
task.fastboot = oldFb;
8278
});
8379

84-
it('can restart multiple times', function() {
85-
const restartPromise = task.restart(options);
86-
return restartPromise
87-
.then(() => task.restart(options))
88-
.then(() => {
89-
expect(restartSpy.callCount).to.equal(2);
90-
expect(stopStub.callCount).to.equal(2);
91-
expect(clearRequireCacheStub.callCount).to.equal(2);
92-
expect(startStub.callCount).to.equal(2);
93-
});
94-
});
95-
96-
// when outputReady while server is starting
97-
// (e.g. app file change during server npm install)
98-
// - wait on start, then reload
99-
// when outputReady multiple times during startup
100-
// (e.g. fast app build, slow server npm install)
101-
// - call reload only once
102-
it('restarts again just once for all calls during startup', function() {
103-
const restartPromise = task.restart(options);
104-
expect(task.restartPromise).to.equal(restartPromise);
105-
expect(task.restartAgain).to.equal(false);
106-
task.restart(options);
107-
task.restart(options);
108-
expect(task.restartPromise).to.equal(restartPromise);
109-
expect(task.restartAgain).to.equal(true);
110-
return restartPromise
111-
.then(() => {
112-
expect(task.restartPromise).to.not.equal(restartPromise);
113-
expect(task.restartAgain).to.equal(false);
114-
return task.restartPromise;
115-
})
80+
it('calls fastboot.reload', function() {
81+
return task.restart(options)
11682
.then(() => {
117-
expect(task.restartPromise).to.equal(null);
118-
expect(task.restartAgain).to.equal(false);
119-
expect(restartSpy.callCount).to.equal(4);
120-
expect(stopStub.callCount).to.equal(2);
121-
expect(clearRequireCacheStub.callCount).to.equal(2);
122-
expect(startStub.callCount).to.equal(2);
123-
});
124-
});
125-
126-
it('can restart again after immediate restart completes', function() {
127-
const restartPromise = task.restart(options);
128-
expect(task.restartPromise).to.equal(restartPromise);
129-
expect(task.restartAgain).to.equal(false);
130-
task.restart(options);
131-
task.restart(options);
132-
expect(task.restartPromise).to.equal(restartPromise);
133-
expect(task.restartAgain).to.equal(true);
134-
return restartPromise
135-
.then(() => {
136-
expect(task.restartPromise).to.not.equal(restartPromise);
137-
expect(task.restartAgain).to.equal(false);
138-
return task.restartPromise;
139-
})
140-
.then(() => {
141-
expect(task.restartPromise).to.equal(null);
142-
expect(task.restartAgain).to.equal(false);
143-
return task.restart(options);
144-
})
145-
.then(() => {
146-
expect(task.restartPromise).to.equal(null);
147-
expect(task.restartAgain).to.equal(false);
148-
expect(restartSpy.callCount).to.equal(5);
149-
expect(stopStub.callCount).to.equal(3);
150-
expect(clearRequireCacheStub.callCount).to.equal(3);
151-
expect(startStub.callCount).to.equal(3);
83+
expect(fbReloadStub.callCount).to.equal(1);
15284
});
15385
});
15486
});
@@ -162,6 +94,7 @@ describe('fastboot server task', function() {
16294
const mockRequire = (which) => {
16395
if (which === 'express') { return mockExpress; }
16496
if (which === 'fastboot-express-middleware') return () => {};
97+
if (which === 'fastboot') return function fakeBoot() {};
16598
};
16699

167100
beforeEach(function() {
@@ -173,21 +106,6 @@ describe('fastboot server task', function() {
173106
useStub = this.sinon.stub(mockApp, 'use');
174107
});
175108

176-
it('runs npm install in server root', function() {
177-
return task.start(options)
178-
.then(() => {
179-
expect(execStub.calledWith('npm install', { cwd: 'dist' })).to.equal(true);
180-
});
181-
});
182-
183-
it('requires server dependencies', function() {
184-
return task.start(options)
185-
.then(() => {
186-
expect(requireSpy.calledWith('fastboot-express-middleware')).to.equal(true);
187-
expect(requireSpy.calledWith('express')).to.equal(true);
188-
});
189-
});
190-
191109
it('uses express.static when serve-assets=true', function() {
192110
options = new CommandOptions({ serveAssets: true });
193111
return task.start(options)

0 commit comments

Comments
 (0)