Skip to content

Commit 708e8da

Browse files
committed
Allow to require module path from whitelisted dependency
Required module path can be one of - `fs` (built-in Node.js module), - `abortcontroller-polyfill` (external main module), - `abortcontroller-polyfill/dist/cjs-ponyfill` (external submodule). FastBoot's build system requires only dependency whitelisting, thus looking only for the first part of module path.
1 parent b79df75 commit 708e8da

File tree

13 files changed

+84989
-10
lines changed

13 files changed

+84989
-10
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"cookie": "^0.3.1",
3131
"debug": "^3.0.0",
3232
"najax": "^1.0.2",
33+
"resolve": "^1.8.1",
3334
"rsvp": "^4.7.0",
3435
"simple-dom": "^1.0.0",
3536
"source-map-support": "^0.5.0"

src/ember-app.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ const chalk = require('chalk');
66

77
const najax = require('najax');
88
const SimpleDOM = require('simple-dom');
9+
const resolve = require('resolve');
910
const debug = require('debug')('fastboot:ember-app');
1011

1112
const FastBootInfo = require('./fastboot-info');
1213
const Result = require('./result');
1314
const FastBootSchemaVersions = require('./fastboot-schema-versions');
15+
const getPackageName = require('./utils/get-package-name');
1416

1517
/**
1618
* @private
@@ -38,6 +40,7 @@ class EmberApp {
3840
this.hostWhitelist = config.hostWhitelist;
3941
this.config = config.config;
4042
this.appName = config.appName;
43+
this.schemaVersion = config.schemaVersion;
4144

4245
if (process.env.APP_CONFIG) {
4346
let appConfig = JSON.parse(process.env.APP_CONFIG);
@@ -114,23 +117,45 @@ class EmberApp {
114117
* @param {string} distPath path to the built Ember app
115118
*/
116119
buildWhitelistedRequire(whitelist, distPath) {
120+
let isLegacyWhitelist = this.schemaVersion < FastBootSchemaVersions.strictWhitelist;
121+
117122
whitelist.forEach(function(whitelistedModule) {
118123
debug("module whitelisted; module=%s", whitelistedModule);
124+
125+
if (isLegacyWhitelist) {
126+
let packageName = getPackageName(whitelistedModule);
127+
128+
if (packageName !== whitelistedModule && whitelist.indexOf(packageName) === -1) {
129+
console.error("Package '" + packageName + "' is required to be in the whitelist.");
130+
}
131+
}
119132
});
120133

121134
return function(moduleName) {
122-
if (whitelist.indexOf(moduleName) > -1) {
123-
let nodeModulesPath = path.join(distPath, 'node_modules', moduleName);
135+
let packageName = getPackageName(moduleName);
136+
let isWhitelisted = whitelist.indexOf(packageName) > -1;
124137

125-
if (fs.existsSync(nodeModulesPath)) {
126-
return require(nodeModulesPath);
138+
if (isWhitelisted) {
139+
let resolvedModulePath = resolve.sync(moduleName, { basedir: distPath });
140+
return require(resolvedModulePath);
141+
}
142+
143+
if (isLegacyWhitelist) {
144+
if (whitelist.indexOf(moduleName) > -1) {
145+
let nodeModulesPath = path.join(distPath, 'node_modules', moduleName);
146+
147+
if (fs.existsSync(nodeModulesPath)) {
148+
return require(nodeModulesPath);
149+
} else {
150+
// If it's not on disk, assume it's a built-in node package
151+
return require(moduleName);
152+
}
127153
} else {
128-
// If it's not on disk, assume it's a built-in node package
129-
return require(moduleName);
154+
throw new Error("Unable to require module '" + moduleName + "' because it was not in the whitelist.");
130155
}
131-
} else {
132-
throw new Error("Unable to require module '" + moduleName + "' because it was not in the whitelist.");
133156
}
157+
158+
throw new Error("Unable to require module '" + moduleName + "' because its package '" + packageName + "' was not in the whitelist.");
134159
};
135160
}
136161

@@ -405,6 +430,7 @@ class EmberApp {
405430
hostWhitelist: pkg.fastboot.hostWhitelist,
406431
config: config,
407432
appName: appName,
433+
schemaVersion: schemaVersion,
408434
};
409435
}
410436

src/fastboot-schema-versions.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
* should be added in fastboot lib) everytime fastboot addon schema version is bumped.
88
*/
99
const FastBootSchemaVersions = {
10-
'latest': 3, // latest schema version supported by fastboot library
10+
'latest': 4, // latest schema version supported by fastboot library
1111
'base': 1, // first schema version supported by fastboot library
1212
'manifestFileArrays': 2, // schema version when app and vendor in manifest supported an array of files
13-
'configExtension': 3 // schema version when FastBoot.config can read arbitrary indexed config
13+
'configExtension': 3, // schema version when FastBoot.config can read arbitrary indexed config
14+
'strictWhitelist': 4 // schema version when fastbootDependencies and whitelist support only package names
1415
};
1516

1617
module.exports = FastBootSchemaVersions;

src/utils/get-package-name.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
function getPackageName(modulePath) {
4+
let parts = modulePath.split('/');
5+
6+
if (modulePath[0] === '@') {
7+
return parts[0] + '/' + parts[1];
8+
}
9+
return parts[0];
10+
}
11+
12+
module.exports = getPackageName;

test/fastboot-dependencies-test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const expect = require('chai').expect;
4+
const fs = require('fs');
5+
const path = require('path');
6+
const fixture = require('./helpers/fixture-path');
7+
const FastBoot = require('./../src/index');
8+
9+
describe.only("FastBoot with dependencies", function() {
10+
it("it works with dependencies", function() {
11+
var fastboot = new FastBoot({
12+
distPath: fixture('app-with-dependencies')
13+
});
14+
15+
return fastboot.visit('/')
16+
.then(r => r.html())
17+
.then(html => {
18+
expect(html).to.match(/https:\/\/emberjs.com/);
19+
expect(html).to.match(/FOO/);
20+
expect(html).to.match(/BAR/);
21+
});
22+
});
23+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
define('app-with-dependencies/initializers/ajax', ['exports'], function (exports) {
2+
'use strict';
3+
4+
Object.defineProperty(exports, "__esModule", {
5+
value: true
6+
});
7+
8+
9+
const { get } = Ember; /* globals najax */
10+
11+
12+
var nodeAjax = function (options) {
13+
let httpRegex = /^https?:\/\//;
14+
let protocolRelativeRegex = /^\/\//;
15+
let protocol = get(this, 'fastboot.request.protocol');
16+
17+
if (protocolRelativeRegex.test(options.url)) {
18+
options.url = protocol + options.url;
19+
} else if (!httpRegex.test(options.url)) {
20+
try {
21+
options.url = protocol + '//' + get(this, 'fastboot.request.host') + options.url;
22+
} catch (fbError) {
23+
throw new Error('You are using Ember Data with no host defined in your adapter. This will attempt to use the host of the FastBoot request, which is not configured for the current host of this request. Please set the hostWhitelist property for in your environment.js. FastBoot Error: ' + fbError.message);
24+
}
25+
}
26+
27+
if (najax) {
28+
najax(options);
29+
} else {
30+
throw new Error('najax does not seem to be defined in your app. Did you override it via `addOrOverrideSandboxGlobals` in the fastboot server?');
31+
}
32+
};
33+
34+
exports.default = {
35+
name: 'ajax-service',
36+
37+
initialize: function (application) {
38+
application.register('ajax:node', nodeAjax, { instantiate: false });
39+
application.inject('adapter', '_ajaxRequest', 'ajax:node');
40+
application.inject('adapter', 'fastboot', 'service:fastboot');
41+
}
42+
};
43+
});
44+
define('app-with-dependencies/initializers/error-handler', ['exports'], function (exports) {
45+
'use strict';
46+
47+
Object.defineProperty(exports, "__esModule", {
48+
value: true
49+
});
50+
exports.default = {
51+
name: 'error-handler',
52+
53+
initialize: function (application) {
54+
if (!Ember.onerror) {
55+
// if no onerror handler is defined, define one for fastboot environments
56+
Ember.onerror = function (err) {
57+
let errorMessage = `There was an error running your app in fastboot. More info about the error: \n ${err.stack || err}`;
58+
Ember.Logger.error(errorMessage);
59+
};
60+
}
61+
}
62+
};
63+
});//# sourceMappingURL=app-with-dependencies-fastboot.map

0 commit comments

Comments
 (0)