Skip to content

Commit 38892b1

Browse files
authored
Merge pull request #356 from kratiahuja/serve-refactor
Unify fastboot serving with ember-cli
2 parents 444f7d2 + ecdb661 commit 38892b1

18 files changed

Lines changed: 861 additions & 308 deletions

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ ember install ember-cli-fastboot
3333
* `ember fastboot --serve-assets`
3434
* Visit your app at `http://localhost:3000`.
3535

36+
**Note**: If your app is running ember-cli v2.12.0-beta.1+, you can just use `ember serve` instead of `ember fastboot --serve-assets`.
37+
3638
You may be shocked to learn that minified code runs faster in Node than
3739
non-minified code, so you will probably want to run the production
3840
environment build for anything "serious."
@@ -381,7 +383,7 @@ present.
381383

382384
### Prototype extensions
383385

384-
Prototype extensions do not currently work across node "realms." Fastboot
386+
Prototype extensions do not currently work across node "realms." Fastboot
385387
applications operate in two realms, a normal node environment and a [virtual machine](https://nodejs.org/api/vm.html). Passing objects that originated from the normal realm will not contain the extension methods
386388
inside of the sandbox environment. For this reason, it's encouraged to [disable prototype extensions](https://guides.emberjs.com/v2.4.0/configuring-ember/disabling-prototype-extensions/).
387389

index.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ var path = require('path');
66
var EventEmitter = require('events').EventEmitter;
77
var mergeTrees = require('broccoli-merge-trees');
88
var VersionChecker = require('ember-cli-version-checker');
9+
var FastBootExpressMiddleware = require('fastboot-express-middleware');
10+
var FastBoot = require('fastboot');
11+
var chalk = require('chalk');
912

1013
var patchEmberApp = require('./lib/ext/patch-ember-app');
1114
var fastbootAppModule = require('./lib/utilities/fastboot-app-module');
@@ -139,12 +142,66 @@ module.exports = {
139142
return fastbootBuild.toTree();
140143
},
141144

145+
serverMiddleware: function(options) {
146+
var emberCliVersion = this._getEmberCliVersion();
147+
var app = options.app;
148+
var options = options.options;
149+
150+
if (emberCliVersion.satisfies('>= 2.12.0-beta.1')) {
151+
// only run the middleware when ember-cli version for app is above 2.12.0-beta.1 since
152+
// that version contains API to hook fastboot into ember-cli
153+
154+
app.use((req, resp, next) => {
155+
var broccoliHeader = req.headers['x-broccoli'];
156+
var outputPath = broccoliHeader['outputPath'];
157+
158+
if (broccoliHeader['url'] === req.serveUrl) {
159+
// if it is a base page request, then have fastboot serve the base page
160+
// TODO(future): provide a way to turn this off without needing to uninstall this addon
161+
if (!this.fastboot) {
162+
// TODO(future): make this configurable for allowing apps to pass sandboxGlobals
163+
// and custom sandbox class
164+
this.ui.writeLine(chalk.green('App is being served by FastBoot'));
165+
this.fastboot = new FastBoot({
166+
distPath: outputPath
167+
});
168+
}
169+
170+
var fastbootMiddleware = FastBootExpressMiddleware({
171+
fastboot: this.fastboot
172+
});
173+
174+
fastbootMiddleware(req, resp, next);
175+
} else {
176+
// forward the request to the next middleware (example other assets, proxy etc)
177+
next();
178+
}
179+
})
180+
}
181+
},
182+
142183
outputReady: function() {
143184
this.emit('outputReady');
144185
},
145186

146-
postBuild: function() {
187+
postBuild: function(result) {
147188
this.emit('postBuild');
189+
if (this.fastboot) {
190+
// should we reload fastboot if there are only css changes? Seems it maynot be needed.
191+
// TODO(future): we can do a smarter reload here by running fs-tree-diff on files loaded
192+
// in sandbox.
193+
this.ui.writeLine(chalk.blue('Reloading FastBoot...'));
194+
this.fastboot.reload({
195+
distPath: result.directory
196+
});
197+
}
198+
},
199+
200+
_getEmberCliVersion: function() {
201+
var VersionChecker = require('ember-cli-version-checker');
202+
var checker = new VersionChecker(this);
203+
204+
return checker.for('ember-cli', 'npm');
148205
},
149206

150207
_getEmberVersion: function() {

lib/commands/fastboot-build.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = {
2020
project: this.project
2121
});
2222

23-
deprecate("Use of ember fastboot:build is deprecated. Please use ember build instead.");
23+
deprecate('Use of ember fastboot:build is deprecated. Please use ember build instead.');
2424

2525
return buildTask.run(options);
2626
},

lib/commands/fastboot.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const RSVP = require('rsvp');
44
const getPort = RSVP.denodeify(require('portfinder').getPort);
55
const ServerTask = require('../tasks/fastboot-server');
66
const SilentError = require('silent-error');
7+
const VersionChecker = require('ember-cli-version-checker');
78

89
const blockForever = () => (new RSVP.Promise(() => {}));
910

@@ -28,12 +29,14 @@ module.exports = function(addon) {
2829
ServerTask,
2930

3031
run(options) {
32+
const printDeprecations = () => this.printDeprecations(options);
3133
const runBuild = () => this.runBuild(options);
3234
const runServer = () => this.runServer(options);
3335
const startServer = (serverTask) => this.startServer(serverTask, options);
3436
const blockForever = this.blockForever;
3537

3638
return this.checkPort(options)
39+
.then(printDeprecations)
3740
.then(runServer) // starts on postBuild SIGHUP
3841
.then(options.build ? runBuild : startServer)
3942
.then(blockForever);
@@ -74,5 +77,16 @@ module.exports = function(addon) {
7477
});
7578
},
7679

80+
printDeprecations(options) {
81+
var checker = new VersionChecker(this);
82+
var dep = checker.for('ember-cli', 'npm');
83+
84+
if (dep.satisfies('>= 2.12.0-beta.1')) {
85+
this.ui.writeDeprecateLine('`ember fastboot --serve-assets` is deprecated. Please use `ember serve` to serve your fastboot assets.');
86+
} else {
87+
this.ui.writeWarnLine('`ember fastboot` will no longer work after FastBoot 1.0 is released.');
88+
}
89+
},
90+
7791
}
7892
};

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@
2323
"broccoli-merge-trees": "^1.1.1",
2424
"broccoli-plugin": "^1.2.1",
2525
"broccoli-stew": "^1.2.0",
26+
"chalk": "^1.1.3",
2627
"compression": "^1.6.2",
2728
"core-object": "^2.0.5",
2829
"debug": "^2.2.0",
2930
"ember-cli-babel": "^5.1.7",
3031
"ember-cli-eslint": "^3.0.2",
31-
"ember-cli-version-checker": "^1.1.6",
32+
"ember-cli-version-checker": "^1.2.0",
3233
"express": "^4.8.5",
3334
"fastboot-express-middleware": "1.0.0-rc.7",
3435
"fastboot-filter-initializers": "0.0.2",
@@ -77,6 +78,9 @@
7778
"configPath": "tests/dummy/config",
7879
"after": [
7980
"broccoli-asset-rev"
81+
],
82+
"before": [
83+
"broccoli-serve-files"
8084
]
8185
}
8286
}

test/async-content-test.js

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,65 @@ var get = RSVP.denodeify(request);
88
describe('async content via deferred content', function() {
99
this.timeout(400000);
1010

11-
var app;
11+
describe('with fastboot command', function() {
12+
var app;
1213

13-
before(function() {
14+
before(function() {
1415

15-
app = new AddonTestApp();
16+
app = new AddonTestApp();
1617

17-
return app.create('async-content')
18-
.then(function() {
19-
return app.startServer({
20-
command: 'fastboot',
21-
additionalArguments: ['--serve-assets']
18+
return app.create('async-content')
19+
.then(function() {
20+
return app.startServer({
21+
command: 'fastboot',
22+
additionalArguments: ['--serve-assets']
23+
});
2224
});
23-
});
24-
});
25+
});
26+
27+
after(function() {
28+
return app.stopServer();
29+
});
2530

26-
after(function() {
27-
return app.stopServer();
31+
it('waits for async content when using `fastboot.deferRendering`', function() {
32+
return get({
33+
url: 'http://localhost:49741/'
34+
})
35+
.then(function(response) {
36+
expect(response.body).to.contain('Async content: foo');
37+
});
38+
});
2839
});
2940

30-
it('waits for async content when using `fastboot.deferRendering`', function() {
31-
return get({
32-
url: 'http://localhost:49741/'
33-
})
34-
.then(function(response) {
35-
expect(response.body).to.contain('Async content: foo');
36-
});
41+
describe('with serve command', function() {
42+
var app;
43+
44+
before(function() {
45+
46+
app = new AddonTestApp();
47+
48+
return app.create('async-content')
49+
.then(function() {
50+
return app.startServer({
51+
command: 'serve'
52+
});
53+
});
54+
});
55+
56+
after(function() {
57+
return app.stopServer();
58+
});
59+
60+
it('waits for async content when using `fastboot.deferRendering`', function() {
61+
return get({
62+
url: 'http://localhost:49741/',
63+
headers: {
64+
'Accept': 'text/html'
65+
}
66+
})
67+
.then(function(response) {
68+
expect(response.body).to.contain('Async content: foo');
69+
});
70+
});
3771
});
3872
});

test/custom-html-file-test.js

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,75 @@
1-
var expect = require('chai').expect;
2-
var RSVP = require('rsvp');
3-
var request = RSVP.denodeify(require('request'));
1+
var expect = require('chai').expect;
2+
var RSVP = require('rsvp');
3+
var request = RSVP.denodeify(require('request'));
44

5-
var AddonTestApp = require('ember-cli-addon-tests').AddonTestApp;
5+
var AddonTestApp = require('ember-cli-addon-tests').AddonTestApp;
66

77
describe('custom htmlFile', function() {
88
this.timeout(400000);
99

10-
var app;
10+
describe('with fastboot command', function() {
11+
var app;
1112

12-
before(function() {
13-
app = new AddonTestApp();
13+
before(function() {
14+
app = new AddonTestApp();
1415

15-
return app.create('custom-html-file')
16-
.then(function() {
17-
return app.startServer({
18-
command: 'fastboot',
19-
additionalArguments: ['--serve-assets']
16+
return app.create('custom-html-file')
17+
.then(function() {
18+
return app.startServer({
19+
command: 'fastboot',
20+
additionalArguments: ['--serve-assets']
21+
});
2022
});
21-
});
22-
});
23+
});
24+
25+
after(function() {
26+
return app.stopServer();
27+
});
28+
29+
it('uses custom htmlFile', function() {
30+
return request('http://localhost:49741/')
31+
.then(function(response) {
32+
expect(response.statusCode).to.equal(200);
33+
expect(response.headers["content-type"]).to.eq("text/html; charset=utf-8");
2334

24-
after(function() {
25-
return app.stopServer();
35+
expect(response.body).to.contain("<title>custom index</title>");
36+
expect(response.body).to.contain("<h1>application template</h1>");
37+
});
38+
});
2639
});
2740

28-
it('uses custom htmlFile', function() {
29-
return request('http://localhost:49741/')
30-
.then(function(response) {
31-
expect(response.statusCode).to.equal(200);
32-
expect(response.headers["content-type"]).to.eq("text/html; charset=utf-8");
41+
describe('with serve command', function() {
42+
var app;
43+
44+
before(function() {
45+
app = new AddonTestApp();
3346

34-
expect(response.body).to.contain("<title>custom index</title>");
35-
expect(response.body).to.contain("<h1>application template</h1>");
36-
});
47+
return app.create('custom-html-file')
48+
.then(function() {
49+
return app.startServer({
50+
command: 'serve'
51+
});
52+
});
53+
});
54+
55+
after(function() {
56+
return app.stopServer();
57+
});
58+
59+
it('uses custom htmlFile', function() {
60+
return request({
61+
url: 'http://localhost:49741/',
62+
headers: {
63+
'Accept': 'text/html'
64+
}
65+
})
66+
.then(function(response) {
67+
expect(response.statusCode).to.equal(200);
68+
expect(response.headers["content-type"]).to.eq("text/html; charset=utf-8");
69+
70+
expect(response.body).to.contain("<title>custom index</title>");
71+
expect(response.body).to.contain("<h1>application template</h1>");
72+
});
73+
});
3774
});
3875
});

0 commit comments

Comments
 (0)