Skip to content

Commit 20030e8

Browse files
committed
Allow custom sandbox to be used in fastboot.
Fastboot had an API to allow any app to use a custom sandbox class. However, this was never fully working. This PR fixes that. This PR also adds a `sandboxGlobals` which allows to pass any other variables that need to be exposed in the sandbox.
1 parent ff877c7 commit 20030e8

8 files changed

Lines changed: 84841 additions & 14 deletions

File tree

src/ember-app.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class EmberApp {
2626
* @param {Object} options
2727
* @param {string} options.distPath - path to the built Ember application
2828
* @param {Sandbox} [options.sandbox=VMSandbox] - sandbox to use
29+
* @param {Object} [options.sandboxGlobals] - sandbox variables that can be added or used for overrides in the sandbox.
2930
*/
3031
constructor(options) {
3132
let distPath = path.resolve(options.distPath);
@@ -43,7 +44,7 @@ class EmberApp {
4344

4445
this.html = fs.readFileSync(config.htmlFile, 'utf8');
4546

46-
this.sandbox = this.buildSandbox(distPath, options.sandbox);
47+
this.sandbox = this.buildSandbox(distPath, options.sandbox, options.sandboxGlobals);
4748
this.app = this.retrieveSandboxedApp();
4849
}
4950

@@ -54,23 +55,32 @@ class EmberApp {
5455
*
5556
* @param {string} distPath path to the built Ember app to load
5657
* @param {Sandbox} [sandboxClass=VMSandbox] sandbox class to use
58+
* @param {Object} [sandboxGlobals={}] any additional variables to expose in the sandbox or override existing in the sandbox
5759
*/
58-
buildSandbox(distPath, sandboxClass) {
60+
buildSandbox(distPath, sandboxClass, sandboxGlobals) {
5961
let Sandbox = sandboxClass || require('./vm-sandbox');
6062
let sandboxRequire = this.buildWhitelistedRequire(this.moduleWhitelist, distPath);
6163
let config = this.appConfig;
6264
function appConfig() {
6365
return { default: config };
6466
}
6567

66-
return new Sandbox({
67-
globals: {
68-
najax: najax,
69-
FastBoot: {
70-
require: sandboxRequire,
71-
config: appConfig
72-
}
68+
// add any additional user provided variables or override the default globals in the sandbox
69+
let globals = {
70+
najax: najax,
71+
FastBoot: {
72+
require: sandboxRequire,
73+
config: appConfig
74+
}
75+
};
76+
for (let key in sandboxGlobals) {
77+
if (sandboxGlobals.hasOwnProperty(key)) {
78+
globals[key] = sandboxGlobals[key];
7379
}
80+
}
81+
82+
return new Sandbox({
83+
globals: globals
7484
});
7585
}
7686

src/index.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ const EmberApp = require('./ember-app');
1515
*
1616
* By default, this sandbox is the built-in `VMSandbox` class, which uses
1717
* Node's `vm` module. You may provide your own sandbox implementation by
18-
* passing the `sandbox` option.
18+
* passing the `sandbox` option or add and/or override sandbox variables by
19+
* passing the `addOrOverrideSandboxGlobals` option.
1920
*
2021
* @example
2122
* const FastBoot = require('fastboot');
2223
*
2324
* let app = new FastBoot({
24-
* distPath: 'path/to/dist'
25+
* distPath: 'path/to/dist',
26+
* sandbox: 'path/to/sandboxClass',
27+
* sandboxGlobals: {...}
2528
* });
2629
*
2730
* app.visit('/photos')
@@ -36,15 +39,17 @@ class FastBoot {
3639
* @param {string} options.distPath the path to the built Ember application
3740
* @param {Boolean} [options.resilient=false] if true, errors during rendering won't reject the `visit()` promise but instead resolve to a {@link Result}
3841
* @param {Sandbox} [options.sandbox=VMSandbox] the sandbox to use
42+
* @param {Object} [options.sandboxGlobals={}] any additional sandbox variables that an app server wants to override and/or add in the sandbox
3943
*/
4044
constructor(options) {
4145
options = options || {};
4246

4347
this.distPath = options.distPath;
4448
this.sandbox = options.sandbox;
49+
this.sandboxGlobals = options.sandboxGlobals || {};
4550
this.resilient = !!options.resilient || false;
4651

47-
this._buildEmberApp(this.distPath);
52+
this._buildEmberApp(this.distPath, this.sandbox, this.sandboxGlobals);
4853
}
4954

5055
/**
@@ -89,7 +94,7 @@ class FastBoot {
8994
this._buildEmberApp(options ? options.distPath : null);
9095
}
9196

92-
_buildEmberApp(distPath) {
97+
_buildEmberApp(distPath, sandbox, sandboxGlobals) {
9398
distPath = distPath || this.distPath;
9499

95100
if (!distPath) {
@@ -104,7 +109,9 @@ class FastBoot {
104109

105110
this.distPath = distPath;
106111
this._app = new EmberApp({
107-
distPath: distPath
112+
distPath: distPath,
113+
sandbox: sandbox,
114+
sandboxGlobals: sandboxGlobals
108115
});
109116
}
110117

test/fastboot-test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const path = require('path');
66
const fixture = require('./helpers/fixture-path');
77
const alchemistRequire = require('broccoli-module-alchemist/require');
88
const FastBoot = alchemistRequire('index');
9+
const CustomSandbox = require('./fixtures/custom-sandbox/custom-sandbox');
910

1011
describe("FastBoot", function() {
1112
it("throws an exception if no distPath is provided", function() {
@@ -91,6 +92,42 @@ describe("FastBoot", function() {
9192
});
9293
});
9394

95+
it("can render HTML when sandboxGlobals is provided", function() {
96+
var fastboot = new FastBoot({
97+
distPath: fixture('custom-sandbox'),
98+
sandboxGlobals: {
99+
foo: 5,
100+
najax: 'undefined',
101+
myVar: 'undefined'
102+
}
103+
});
104+
105+
return fastboot.visit('/foo')
106+
.then(r => r.html())
107+
.then(html => {
108+
expect(html).to.match(/foo from sandbox: 5/);
109+
expect(html).to.match(/najax in sandbox: undefined/);
110+
});
111+
});
112+
113+
it("can render HTML when sandbox class is provided", function() {
114+
var fastboot = new FastBoot({
115+
distPath: fixture('custom-sandbox'),
116+
sandboxClass: CustomSandbox,
117+
sandboxGlobals: {
118+
myVar: 2,
119+
foo: 'undefined',
120+
najax: 'undefined'
121+
}
122+
});
123+
124+
return fastboot.visit('/foo')
125+
.then(r => r.html())
126+
.then(html => {
127+
expect(html).to.match(/myVar in sandbox: 2/);
128+
});
129+
});
130+
94131
it("rejects the promise if an error occurs", function() {
95132
var fastboot = new FastBoot({
96133
distPath: fixture('rejected-promise')

0 commit comments

Comments
 (0)