Comparing version 2.0.3 to 3.0.0-beta.1
module.exports = { | ||
'env': { | ||
'browser': true, | ||
'node': true, | ||
'es6': true, | ||
parserOptions: { | ||
ecmaVersion: 2017, | ||
}, | ||
extends: ['eslint:recommended', 'plugin:node/recommended', 'plugin:prettier/recommended'], | ||
plugins: ['prettier', 'node'], | ||
env: { | ||
browser: true, | ||
node: true, | ||
es6: true, | ||
}, | ||
rules: { | ||
'no-console': ['error', { allow: ['warn', 'error'] }], | ||
}, | ||
overrides: [ | ||
// override eslint in the dev/* folder to allow features from more recent | ||
// Node versions | ||
{ | ||
files: ['dev/**/*.js'], | ||
rules: { | ||
'node/no-unsupported-features/node-builtins': [ | ||
'error', | ||
{ | ||
version: '>=10.0.0', | ||
ignores: [], | ||
}, | ||
], | ||
}, | ||
}, | ||
'extends': ['eslint:recommended', 'plugin:node/recommended'], | ||
'parserOptions': { | ||
'ecmaVersion': 6 | ||
}, | ||
'rules': { | ||
'no-console': ['error', { 'allow': ['warn', 'error']}], | ||
'linebreak-style': [ | ||
'error', | ||
'unix' | ||
], | ||
'semi': [ | ||
'error', | ||
'always' | ||
] | ||
} | ||
}; | ||
], | ||
}; |
@@ -0,1 +1,33 @@ | ||
## v3.0.0-beta.1 (2019-10-30) | ||
#### :boom: Breaking Change | ||
* [#236](https://github.com/ember-fastboot/fastboot/pull/236) Refactor to use a single sandboxed context per visit request. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#225](https://github.com/ember-fastboot/fastboot/pull/225) Drop support for Node 6, 9, and 11. ([@kiwiupover](https://github.com/kiwiupover)) | ||
#### :rocket: Enhancement | ||
* [#229](https://github.com/ember-fastboot/fastboot/pull/229) Add `FastBoot.distPath` ([@stefanpenner](https://github.com/stefanpenner)) | ||
#### :bug: Bug Fix | ||
* [#227](https://github.com/ember-fastboot/fastboot/pull/227) Restore allowing fallback require from working directory ([@xg-wang](https://github.com/xg-wang)) | ||
* [#219](https://github.com/ember-fastboot/fastboot/pull/219) Fix an incorrect `debug()` call ([@CvX](https://github.com/CvX)) | ||
#### :memo: Documentation | ||
* [#235](https://github.com/ember-fastboot/fastboot/pull/235) Document `reload` method. ([@rwjblue](https://github.com/rwjblue)) | ||
#### :house: Internal | ||
* [#243](https://github.com/ember-fastboot/fastboot/pull/243) Add automated release setup. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#238](https://github.com/ember-fastboot/fastboot/pull/238) Add basic memory profiling script to `dev/` folder. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#237](https://github.com/ember-fastboot/fastboot/pull/237) Add dev script to make tracing easier. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#234](https://github.com/ember-fastboot/fastboot/pull/234) Remove `rsvp` dependency. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#233](https://github.com/ember-fastboot/fastboot/pull/233) Update dependencies/devDependencies to latest. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#232](https://github.com/ember-fastboot/fastboot/pull/232) Make a single `Sandbox` base class. ([@rwjblue](https://github.com/rwjblue)) | ||
* [#231](https://github.com/ember-fastboot/fastboot/pull/231) General repo cleanup. ([@rwjblue](https://github.com/rwjblue)) | ||
#### Committers: 5 | ||
- David Laird ([@kiwiupover](https://github.com/kiwiupover)) | ||
- Jarek Radosz ([@CvX](https://github.com/CvX)) | ||
- Robert Jackson ([@rwjblue](https://github.com/rwjblue)) | ||
- Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) | ||
- Thomas Wang ([@xg-wang](https://github.com/xg-wang)) | ||
# FastBoot Changelog | ||
@@ -2,0 +34,0 @@ |
{ | ||
"name": "fastboot", | ||
"version": "2.0.3", | ||
"version": "3.0.0-beta.1", | ||
"description": "Library for rendering Ember apps in node.js", | ||
"keywords": [ | ||
"ember", | ||
"fastboot" | ||
], | ||
"homepage": "https://github.com/ember-fastboot/fastboot#readme", | ||
"bugs": { | ||
"url": "https://github.com/ember-fastboot/fastboot/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/ember-fastboot/fastboot.git" | ||
}, | ||
"license": "MIT", | ||
"author": "Tom Dale and FastBoot contributors", | ||
"main": "src/index.js", | ||
"scripts": { | ||
"changelog": "lerna-changelog", | ||
"lint:js": "node_modules/.bin/eslint src test", | ||
"lint:js": "eslint --cache .", | ||
"test": "npm run-script lint:js && mocha", | ||
@@ -13,43 +27,49 @@ "preversion": "npm test", | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/ember-fastboot/fastboot.git" | ||
}, | ||
"engines": { | ||
"node": "^6.14.0 || ^8.10.0 || >=9.10.0" | ||
}, | ||
"keywords": [ | ||
"ember", | ||
"fastboot" | ||
], | ||
"author": "Tom Dale and FastBoot contributors", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/ember-fastboot/fastboot/issues" | ||
}, | ||
"homepage": "https://github.com/ember-fastboot/fastboot#readme", | ||
"dependencies": { | ||
"chalk": "^2.0.1", | ||
"chalk": "^2.4.2", | ||
"cookie": "^0.4.0", | ||
"debug": "^4.1.0", | ||
"najax": "^1.0.3", | ||
"resolve": "^1.8.1", | ||
"rsvp": "^4.8.0", | ||
"debug": "^4.1.1", | ||
"najax": "^1.0.4", | ||
"resolve": "^1.12.0", | ||
"simple-dom": "^1.4.0", | ||
"source-map-support": "^0.5.0" | ||
"source-map-support": "^0.5.13" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.1.0", | ||
"chai": "^4.2.0", | ||
"chai-as-promised": "^7.1.1", | ||
"ember-source": "3.8.0", | ||
"eslint": "^5.10.0", | ||
"eslint": "^6.5.1", | ||
"eslint-config-prettier": "^6.4.0", | ||
"eslint-plugin-chai-expect": "^2.0.1", | ||
"eslint-plugin-mocha": "^5.2.0", | ||
"eslint-plugin-node": "^8.0.0", | ||
"express": "^4.15.4", | ||
"eslint-plugin-mocha": "^6.2.0", | ||
"eslint-plugin-node": "^10.0.0", | ||
"eslint-plugin-prettier": "^3.1.1", | ||
"express": "^4.17.1", | ||
"lerna-changelog": "^0.8.2", | ||
"mocha": "^5.2.0", | ||
"rimraf": "^2.6.2", | ||
"temp": "^0.8.3" | ||
"mocha": "^6.2.2", | ||
"prettier": "^1.18.2", | ||
"release-it": "^12.2.1", | ||
"release-it-lerna-changelog": "^1.0.3", | ||
"rimraf": "^3.0.0", | ||
"temp": "^0.9.0" | ||
}, | ||
"engines": { | ||
"node": "^8.10.0 || 10.* || >=12" | ||
}, | ||
"publishConfig": { | ||
"registry": "https://registry.npmjs.org" | ||
}, | ||
"release-it": { | ||
"plugins": { | ||
"release-it-lerna-changelog": { | ||
"infile": "CHANGELOG.md" | ||
} | ||
}, | ||
"git": { | ||
"tagName": "v${version}" | ||
}, | ||
"github": { | ||
"release": true | ||
} | ||
} | ||
} |
@@ -31,3 +31,2 @@ # FastBoot | ||
resilient: <boolean>, | ||
sandbox: 'path/to/sandbox/class', // optional sandbox class (defaults to vm-sandbox) | ||
sandboxGlobals: {...} // optional map of key value pairs to expose in the sandbox | ||
@@ -34,0 +33,0 @@ }); |
'use strict'; | ||
const fs = require('fs'); | ||
const vm = require('vm'); | ||
const path = require('path'); | ||
@@ -12,2 +13,3 @@ const chalk = require('chalk'); | ||
const Sandbox = require('./sandbox'); | ||
const FastBootInfo = require('./fastboot-info'); | ||
@@ -31,11 +33,11 @@ const Result = require('./result'); | ||
* @param {string} options.distPath - path to the built Ember application | ||
* @param {Sandbox} [options.sandbox=VMSandbox] - sandbox to use | ||
* @param {Object} [options.sandboxGlobals] - sandbox variables that can be added or used for overrides in the sandbox. | ||
*/ | ||
constructor(options) { | ||
let distPath = path.resolve(options.distPath); | ||
// TODO: make these two into builder functions | ||
this.sandboxGlobals = options.sandboxGlobals; | ||
let distPath = (this.distPath = path.resolve(options.distPath)); | ||
let config = this.readPackageJSON(distPath); | ||
this.appFilePaths = config.appFiles; | ||
this.vendorFilePaths = config.vendorFiles; | ||
this.moduleWhitelist = config.moduleWhitelist; | ||
@@ -50,3 +52,3 @@ this.hostWhitelist = config.hostWhitelist; | ||
let appConfigKey = this.appName; | ||
if (!appConfig.hasOwnProperty(appConfigKey)) { | ||
if (!(appConfigKey in appConfig)) { | ||
this.config[appConfigKey] = appConfig; | ||
@@ -63,4 +65,12 @@ } | ||
this.sandbox = this.buildSandbox(distPath, options.sandbox, options.sandboxGlobals); | ||
this.app = this.retrieveSandboxedApp(); | ||
this.sandboxRequire = this.buildWhitelistedRequire(this.moduleWhitelist, distPath); | ||
let filePaths = [require.resolve('./scripts/install-source-map-support')].concat( | ||
config.vendorFiles, | ||
config.appFiles | ||
); | ||
this.scripts = buildScripts(filePaths); | ||
// Ensure that the dist files can be evaluated and the `Ember.Application` | ||
// instance created. | ||
this.buildApp(); | ||
} | ||
@@ -72,11 +82,6 @@ | ||
* Builds and initializes a new sandbox to run the Ember application in. | ||
* | ||
* @param {string} distPath path to the built Ember app to load | ||
* @param {Sandbox} [sandboxClass=VMSandbox] sandbox class to use | ||
* @param {Object} [sandboxGlobals={}] any additional variables to expose in the sandbox or override existing in the sandbox | ||
*/ | ||
buildSandbox(distPath, sandboxClass, sandboxGlobals) { | ||
let sandboxRequire = this.buildWhitelistedRequire(this.moduleWhitelist, distPath); | ||
const config = this.config; | ||
const appName = this.appName; | ||
buildSandbox() { | ||
const { distPath, sandboxGlobals, config, appName, sandboxRequire } = this; | ||
function fastbootConfig(key) { | ||
@@ -96,16 +101,18 @@ if (!key) { | ||
// add any additional user provided variables or override the default globals in the sandbox | ||
let globals = { | ||
najax, | ||
FastBoot: { | ||
require: sandboxRequire, | ||
config: fastbootConfig, | ||
get distPath() { | ||
return distPath; | ||
} | ||
} | ||
}; | ||
let globals = Object.assign( | ||
{ | ||
najax, | ||
FastBoot: { | ||
require: sandboxRequire, | ||
config: fastbootConfig, | ||
globals = Object.assign(globals, sandboxGlobals); | ||
get distPath() { | ||
return distPath; | ||
}, | ||
}, | ||
}, | ||
sandboxGlobals | ||
); | ||
return new sandboxClass({ globals }); | ||
return new Sandbox(globals); | ||
} | ||
@@ -132,3 +139,3 @@ | ||
whitelist.forEach(function(whitelistedModule) { | ||
debug("module whitelisted; module=%s", whitelistedModule); | ||
debug('module whitelisted; module=%s', whitelistedModule); | ||
@@ -139,3 +146,3 @@ if (isLegacyWhitelist) { | ||
if (packageName !== whitelistedModule && whitelist.indexOf(packageName) === -1) { | ||
console.error("Package '" + packageName + "' is required to be in the whitelist."); | ||
console.error("Package '" + packageName + "' is required to be in the whitelist."); | ||
} | ||
@@ -172,7 +179,15 @@ } | ||
} else { | ||
throw new Error("Unable to require module '" + moduleName + "' because it was not in the whitelist."); | ||
throw new Error( | ||
"Unable to require module '" + moduleName + "' because it was not in the whitelist." | ||
); | ||
} | ||
} | ||
throw new Error("Unable to require module '" + moduleName + "' because its package '" + packageName + "' was not in the whitelist."); | ||
throw new Error( | ||
"Unable to require module '" + | ||
moduleName + | ||
"' because its package '" + | ||
packageName + | ||
"' was not in the whitelist." | ||
); | ||
}; | ||
@@ -182,29 +197,7 @@ } | ||
/** | ||
* @private | ||
* | ||
* Loads the app and vendor files in the sandbox (Node vm). | ||
* | ||
*/ | ||
loadAppFiles() { | ||
let sandbox = this.sandbox; | ||
let appFilePaths = this.appFilePaths; | ||
let vendorFilePaths = this.vendorFilePaths; | ||
sandbox.eval('sourceMapSupport.install(Error);'); | ||
debug("evaluating app and vendor files"); | ||
vendorFilePaths.forEach(function(vendorFilePath) { | ||
debug("evaluating vendor file %s", vendorFilePath); | ||
let vendorFile = fs.readFileSync(vendorFilePath, 'utf8'); | ||
sandbox.eval(vendorFile, vendorFilePath); | ||
}); | ||
debug("vendor file evaluated"); | ||
appFilePaths.forEach(function(appFilePath) { | ||
debug("evaluating app file %s", appFilePath); | ||
let appFile = fs.readFileSync(appFilePath, 'utf8'); | ||
sandbox.eval(appFile, appFilePath); | ||
}); | ||
debug("app files evaluated"); | ||
* Perform any cleanup that is needed | ||
*/ | ||
destroy() { | ||
// TODO: expose as public api (through the top level) so that we can | ||
// cleanup pre-warmed visits | ||
} | ||
@@ -215,8 +208,18 @@ | ||
* | ||
* Create the ember application in the sandbox. | ||
* Creates a new `Application` | ||
* | ||
* @returns {Ember.Application} instance | ||
*/ | ||
createEmberApp() { | ||
let sandbox = this.sandbox; | ||
buildApp() { | ||
let sandbox = this.buildSandbox(); | ||
debug('adding files to sandbox'); | ||
for (let script of this.scripts) { | ||
debug('evaluating file %s', script); | ||
sandbox.runScript(script); | ||
} | ||
debug('files evaluated'); | ||
// Retrieve the application factory from within the sandbox | ||
@@ -229,52 +232,18 @@ let AppFactory = sandbox.run(function(ctx) { | ||
if (!AppFactory || typeof AppFactory['default'] !== 'function') { | ||
throw new Error('Failed to load Ember app from app.js, make sure it was built for FastBoot with the `ember fastboot:build` command.'); | ||
throw new Error( | ||
'Failed to load Ember app from app.js, make sure it was built for FastBoot with the `ember fastboot:build` command.' | ||
); | ||
} | ||
debug('creating application'); | ||
// Otherwise, return a new `Ember.Application` instance | ||
return AppFactory['default'](); | ||
} | ||
let app = AppFactory['default'](); | ||
/** | ||
* @private | ||
* | ||
* Initializes the sandbox by evaluating the Ember app's JavaScript | ||
* code, then retrieves the application factory from the sandbox and creates a new | ||
* `Ember.Application`. | ||
* | ||
* @returns {Ember.Application} the Ember application from the sandbox | ||
*/ | ||
retrieveSandboxedApp() { | ||
this.loadAppFiles(); | ||
return this.createEmberApp(); | ||
return app; | ||
} | ||
/** | ||
* Destroys the app and its sandbox. | ||
*/ | ||
destroy() { | ||
if (this.app) { | ||
this.app.destroy(); | ||
} | ||
this.sandbox = null; | ||
} | ||
/** | ||
* @private | ||
* | ||
* Creates a new `ApplicationInstance` from the sandboxed `Application`. | ||
* | ||
* @returns {Promise<Ember.ApplicationInstance>} instance | ||
*/ | ||
buildAppInstance() { | ||
return this.app.boot().then(function(app) { | ||
debug('building instance'); | ||
return app.buildInstance(); | ||
}); | ||
} | ||
/** | ||
* @private | ||
* | ||
* Main function that creates the app instance for every `visit` request, boots | ||
@@ -295,16 +264,16 @@ * the app instance and then visits the given route and destroys the app instance | ||
*/ | ||
visitRoute(path, fastbootInfo, bootOptions, result) { | ||
let instance; | ||
async visitRoute(path, fastbootInfo, bootOptions, result) { | ||
let app = await this.buildApp(); | ||
result.applicationInstance = app; | ||
return this.buildAppInstance() | ||
.then(appInstance => { | ||
instance = appInstance; | ||
result.instance = instance; | ||
registerFastBootInfo(fastbootInfo, instance); | ||
await app.boot(); | ||
return instance.boot(bootOptions); | ||
}) | ||
.then(() => instance.visit(path, bootOptions)) | ||
.then(() => fastbootInfo.deferredPromise) | ||
.then(() => instance); | ||
let instance = await app.buildInstance(); | ||
result.applicationInstanceInstance = instance; | ||
registerFastBootInfo(fastbootInfo, instance); | ||
await instance.boot(bootOptions); | ||
await instance.visit(path, bootOptions); | ||
await fastbootInfo.deferredPromise; | ||
} | ||
@@ -334,3 +303,3 @@ | ||
*/ | ||
visit(path, options) { | ||
async visit(path, options) { | ||
let req = options.request; | ||
@@ -342,18 +311,13 @@ let res = options.response; | ||
let shouldRender = (options.shouldRender !== undefined) ? options.shouldRender : true; | ||
let shouldRender = options.shouldRender !== undefined ? options.shouldRender : true; | ||
let bootOptions = buildBootOptions(shouldRender); | ||
let fastbootInfo = new FastBootInfo( | ||
req, | ||
res, | ||
{ hostWhitelist: this.hostWhitelist, metadata: options.metadata } | ||
); | ||
let fastbootInfo = new FastBootInfo(req, res, { | ||
hostWhitelist: this.hostWhitelist, | ||
metadata: options.metadata, | ||
}); | ||
let doc = bootOptions.document; | ||
let result = new Result(doc, html, fastbootInfo); | ||
let result = new Result({ | ||
doc: doc, | ||
html: html, | ||
fastbootInfo: fastbootInfo | ||
}); | ||
// TODO: Use Promise.race here | ||
let destroyAppInstanceTimer; | ||
@@ -364,4 +328,6 @@ if (destroyAppInstanceInMs > 0) { | ||
destroyAppInstanceTimer = setTimeout(function() { | ||
if (result._destroyAppInstance()) { | ||
result.error = new Error('App instance was forcefully destroyed in ' + destroyAppInstanceInMs + 'ms'); | ||
if (result._destroy()) { | ||
result.error = new Error( | ||
'App instance was forcefully destroyed in ' + destroyAppInstanceInMs + 'ms' | ||
); | ||
} | ||
@@ -371,18 +337,24 @@ }, destroyAppInstanceInMs); | ||
return this.visitRoute(path, fastbootInfo, bootOptions, result) | ||
.then(() => { | ||
if (!disableShoebox) { | ||
// if shoebox is not disabled, then create the shoebox and send API data | ||
createShoebox(doc, fastbootInfo); | ||
} | ||
}) | ||
.catch(error => result.error = error) | ||
.then(() => result._finalize()) | ||
.finally(() => { | ||
if (result._destroyAppInstance()) { | ||
if (destroyAppInstanceTimer) { | ||
clearTimeout(destroyAppInstanceTimer); | ||
} | ||
} | ||
}); | ||
try { | ||
await this.visitRoute(path, fastbootInfo, bootOptions, result); | ||
if (!disableShoebox) { | ||
// if shoebox is not disabled, then create the shoebox and send API data | ||
createShoebox(doc, fastbootInfo); | ||
} | ||
result._finalize(); | ||
} catch (error) { | ||
// eslint-disable-next-line require-atomic-updates | ||
result.error = error; | ||
} finally { | ||
// ensure we invoke `Ember.Application.destroy()` and | ||
// `Ember.ApplicationInstance.destroy()`, but use `result._destroy()` so | ||
// that the `result` object's internal `this.isDestroyed` flag is correct | ||
result._destroy(); | ||
clearTimeout(destroyAppInstanceTimer); | ||
} | ||
return result; | ||
} | ||
@@ -401,3 +373,5 @@ | ||
} catch (e) { | ||
throw new Error(`Couldn't find ${pkgPath}. You may need to update your version of ember-cli-fastboot.`); | ||
throw new Error( | ||
`Couldn't find ${pkgPath}. You may need to update your version of ember-cli-fastboot.` | ||
); | ||
} | ||
@@ -414,3 +388,5 @@ | ||
} catch (e) { | ||
throw new Error(`${pkgPath} was malformed or did not contain a manifest. Ensure that you have a compatible version of ember-cli-fastboot.`); | ||
throw new Error( | ||
`${pkgPath} was malformed or did not contain a manifest. Ensure that you have a compatible version of ember-cli-fastboot.` | ||
); | ||
} | ||
@@ -421,5 +397,11 @@ | ||
schemaVersion = schemaVersion || FastBootSchemaVersions.base; | ||
debug('Current schemaVersion from `ember-cli-fastboot` is %s while latest schema version is %s', schemaVersion, currentSchemaVersion); | ||
debug( | ||
'Current schemaVersion from `ember-cli-fastboot` is %s while latest schema version is %s', | ||
schemaVersion, | ||
currentSchemaVersion | ||
); | ||
if (schemaVersion > currentSchemaVersion) { | ||
let errorMsg = chalk.bold.red('An incompatible version between `ember-cli-fastboot` and `fastboot` was found. Please update the version of fastboot library that is compatible with ember-cli-fastboot.'); | ||
let errorMsg = chalk.bold.red( | ||
'An incompatible version between `ember-cli-fastboot` and `fastboot` was found. Please update the version of fastboot library that is compatible with ember-cli-fastboot.' | ||
); | ||
throw new Error(errorMsg); | ||
@@ -444,9 +426,9 @@ } | ||
debug("reading array of app file paths from manifest"); | ||
var appFiles = manifest.appFiles.map(function(appFile) { | ||
debug('reading array of app file paths from manifest'); | ||
let appFiles = manifest.appFiles.map(function(appFile) { | ||
return path.join(distPath, appFile); | ||
}); | ||
debug("reading array of vendor file paths from manifest"); | ||
var vendorFiles = manifest.vendorFiles.map(function(vendorFile) { | ||
debug('reading array of vendor file paths from manifest'); | ||
let vendorFiles = manifest.vendorFiles.map(function(vendorFile) { | ||
return path.join(distPath, vendorFile); | ||
@@ -456,10 +438,10 @@ }); | ||
return { | ||
appFiles: appFiles, | ||
vendorFiles: vendorFiles, | ||
appFiles, | ||
vendorFiles, | ||
htmlFile: path.join(distPath, manifest.htmlFile), | ||
moduleWhitelist: pkg.fastboot.moduleWhitelist, | ||
hostWhitelist: pkg.fastboot.hostWhitelist, | ||
config: config, | ||
appName: appName, | ||
schemaVersion: schemaVersion, | ||
config, | ||
appName, | ||
schemaVersion, | ||
}; | ||
@@ -493,3 +475,3 @@ } | ||
shouldRender, | ||
_renderMode | ||
_renderMode, | ||
}; | ||
@@ -510,6 +492,10 @@ } | ||
let shoebox = fastbootInfo.shoebox; | ||
if (!shoebox) { return; } | ||
if (!shoebox) { | ||
return; | ||
} | ||
for (let key in shoebox) { | ||
if (!hasOwnProperty.call(shoebox, key)) { continue; } // TODO: remove this later #144, ember-fastboot/ember-cli-fastboot/pull/417 | ||
if (!hasOwnProperty.call(shoebox, key)) { | ||
continue; | ||
} // TODO: remove this later #144, ember-fastboot/ember-cli-fastboot/pull/417 | ||
let value = shoebox[key]; | ||
@@ -534,3 +520,3 @@ let textValue = JSON.stringify(value); | ||
'\u2028': '\\u2028', | ||
'\u2029': '\\u2029' | ||
'\u2029': '\\u2029', | ||
}; | ||
@@ -554,2 +540,9 @@ | ||
function buildScripts(filePaths) { | ||
return filePaths.filter(Boolean).map(filePath => { | ||
let source = fs.readFileSync(filePath, { encoding: 'utf8' }); | ||
return new vm.Script(source, { filename: filePath }); | ||
}); | ||
} | ||
module.exports = EmberApp; |
@@ -9,3 +9,3 @@ 'use strict'; | ||
for (var header in headers) { | ||
for (let header in headers) { | ||
let value = headers[header]; | ||
@@ -40,7 +40,7 @@ | ||
entries() { | ||
var entries = []; | ||
let entries = []; | ||
for(var key in this.headers) { | ||
var values = this.headers[key]; | ||
for(var index = 0; index < values.length; ++index ) { | ||
for (let key in this.headers) { | ||
let values = this.headers[key]; | ||
for (let index = 0; index < values.length; ++index) { | ||
entries.push([key, values[index]]); | ||
@@ -66,7 +66,7 @@ } | ||
keys() { | ||
var entries = []; | ||
let entries = []; | ||
for(var key in this.headers) { | ||
var values = this.headers[key]; | ||
for(var index = 0; index < values.length; ++index ) { | ||
for (let key in this.headers) { | ||
let values = this.headers[key]; | ||
for (let index = 0; index < values.length; ++index) { | ||
entries.push(key); | ||
@@ -85,7 +85,7 @@ } | ||
values() { | ||
var entries = []; | ||
let entries = []; | ||
for(var key in this.headers) { | ||
var values = this.headers[key]; | ||
for(var index = 0; index < values.length; ++index ) { | ||
for (let key in this.headers) { | ||
let values = this.headers[key]; | ||
for (let index = 0; index < values.length; ++index) { | ||
entries.push(values[index]); | ||
@@ -99,3 +99,5 @@ } | ||
unknownProperty(maybeHeader) { | ||
console.warn(`You called \`Ember.get(headers, '${maybeHeader}')\` with a FastBootHeaders instance as first argument. FastBootHeader is not an Ember object and you should use \`headers.get('${maybeHeader}')\` instead.`); | ||
console.warn( | ||
`You called \`Ember.get(headers, '${maybeHeader}')\` with a FastBootHeaders instance as first argument. FastBootHeader is not an Ember object and you should use \`headers.get('${maybeHeader}')\` instead.` | ||
); | ||
return this.get(maybeHeader); | ||
@@ -102,0 +104,0 @@ } |
'use strict'; | ||
const RSVP = require('rsvp'); | ||
const FastBootRequest = require('./fastboot-request'); | ||
@@ -18,7 +17,7 @@ const FastBootResponse = require('./fastboot-response'); | ||
*/ | ||
class FastBootInfo { | ||
module.exports = class FastBootInfo { | ||
constructor(request, response, options) { | ||
this.deferredPromise = RSVP.resolve(); | ||
let hostWhitelist = options.hostWhitelist; | ||
let metadata = options.metadata; | ||
this.deferredPromise = Promise.resolve(); | ||
let { hostWhitelist, metadata } = options; | ||
if (request) { | ||
@@ -33,5 +32,3 @@ this.request = new FastBootRequest(request, hostWhitelist); | ||
deferRendering(promise) { | ||
this.deferredPromise = this.deferredPromise.then(function() { | ||
return promise; | ||
}); | ||
this.deferredPromise = Promise.all(this.deferredPromise, promise); | ||
} | ||
@@ -48,5 +45,2 @@ | ||
} | ||
} | ||
module.exports = FastBootInfo; | ||
}; |
@@ -58,6 +58,4 @@ 'use strict'; | ||
} | ||
} | ||
module.exports = FastBootRequest; |
@@ -10,9 +10,9 @@ /** | ||
const FastBootSchemaVersions = { | ||
'latest': 4, // latest schema version supported by fastboot library | ||
'base': 1, // first schema version supported by fastboot library | ||
'manifestFileArrays': 2, // schema version when app and vendor in manifest supported an array of files | ||
'configExtension': 3, // schema version when FastBoot.config can read arbitrary indexed config | ||
'strictWhitelist': 4 // schema version when fastbootDependencies and whitelist support only package names | ||
latest: 4, // latest schema version supported by fastboot library | ||
base: 1, // first schema version supported by fastboot library | ||
manifestFileArrays: 2, // schema version when app and vendor in manifest supported an array of files | ||
configExtension: 3, // schema version when FastBoot.config can read arbitrary indexed config | ||
strictWhitelist: 4, // schema version when fastbootDependencies and whitelist support only package names | ||
}; | ||
module.exports = FastBootSchemaVersions; |
@@ -16,5 +16,4 @@ 'use strict'; | ||
* | ||
* By default, this sandbox is the built-in `VMSandbox` class, which uses | ||
* Node's `vm` module. You may provide your own sandbox implementation by | ||
* passing the `sandbox` option or add and/or override sandbox variables by | ||
* This sandbox is the built-in `VMSandbox` class, which uses | ||
* Node's `vm` module. You may add and/or override sandbox variables by | ||
* passing the `addOrOverrideSandboxGlobals` option. | ||
@@ -27,3 +26,2 @@ * | ||
* distPath: 'path/to/dist', | ||
* sandbox: 'path/to/sandboxClass', | ||
* sandboxGlobals: {...} | ||
@@ -43,14 +41,13 @@ * }); | ||
* @param {Boolean} [options.resilient=false] if true, errors during rendering won't reject the `visit()` promise but instead resolve to a {@link Result} | ||
* @param {Sandbox} [options.sandbox=VMSandbox] the sandbox to use | ||
* @param {Object} [options.sandboxGlobals={}] any additional sandbox variables that an app server wants to override and/or add in the sandbox | ||
*/ | ||
constructor(options) { | ||
options = options || {}; | ||
constructor(options = {}) { | ||
let { distPath, sandboxGlobals } = options; | ||
this.distPath = options.distPath; | ||
this.sandbox = options.sandbox || require('./vm-sandbox'); | ||
this.sandboxGlobals = options.sandboxGlobals || {}; | ||
this.resilient = !!options.resilient || false; | ||
this.resilient = 'resilient' in options ? Boolean(options.resilient) : false; | ||
this._buildEmberApp(this.distPath, this.sandbox, this.sandboxGlobals); | ||
this.distPath = distPath; | ||
this.sandboxGlobals = sandboxGlobals || {}; | ||
this._buildEmberApp(this.distPath, this.sandboxGlobals); | ||
} | ||
@@ -73,22 +70,23 @@ | ||
*/ | ||
visit(path, options) { | ||
options = options || {}; | ||
async visit(path, options = {}) { | ||
let resilient = 'resilient' in options ? options.resilient : this.resilient; | ||
let resilient = options.resilient; | ||
let result = await this._app.visit(path, options); | ||
if (resilient === undefined) { | ||
resilient = this.resilient; | ||
if (!resilient && result.error) { | ||
throw result.error; | ||
} else { | ||
return result; | ||
} | ||
return this._app.visit(path, options) | ||
.then(result => { | ||
if (!resilient && result.error) { | ||
throw result.error; | ||
} else { | ||
return result; | ||
} | ||
}); | ||
} | ||
reload(options) { | ||
/** | ||
* Destroy the existing Ember application instance, and recreate it from the provided dist path. | ||
* This is commonly done when `dist` has been updated, and you need to prepare to serve requests | ||
* with the updated assets. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.distPath the path to the built Ember application | ||
*/ | ||
reload({ distPath }) { | ||
if (this._app) { | ||
@@ -98,31 +96,27 @@ this._app.destroy(); | ||
options = options || {}; | ||
this._buildEmberApp(options.distPath || null); | ||
this._buildEmberApp(distPath); | ||
} | ||
_buildEmberApp(distPath, sandbox, sandboxGlobals) { | ||
distPath = distPath || this.distPath; | ||
sandbox = sandbox || this.sandbox; | ||
sandboxGlobals = sandboxGlobals || this.sandboxGlobals; | ||
_buildEmberApp(distPath = this.distPath, sandboxGlobals = this.sandboxGlobals) { | ||
if (!distPath) { | ||
throw new Error('You must instantiate FastBoot with a distPath ' + | ||
'option that contains a path to a dist directory ' + | ||
'produced by running ember fastboot:build in your Ember app:' + | ||
'\n\n' + | ||
'new FastBootServer({\n' + | ||
' distPath: \'path/to/dist\'\n' + | ||
'});'); | ||
throw new Error( | ||
'You must instantiate FastBoot with a distPath ' + | ||
'option that contains a path to a dist directory ' + | ||
'produced by running ember fastboot:build in your Ember app:' + | ||
'\n\n' + | ||
'new FastBootServer({\n' + | ||
" distPath: 'path/to/dist'\n" + | ||
'});' | ||
); | ||
} | ||
this.distPath = distPath; | ||
this._app = new EmberApp({ | ||
distPath: distPath, | ||
sandbox: sandbox, | ||
sandboxGlobals: sandboxGlobals | ||
distPath, | ||
sandboxGlobals, | ||
}); | ||
} | ||
} | ||
module.exports = FastBoot; |
@@ -15,7 +15,10 @@ 'use strict'; | ||
class Result { | ||
constructor(options) { | ||
this._instanceDestroyed = false; | ||
this._doc = options.doc; | ||
this._html = options.html; | ||
this._fastbootInfo = options.fastbootInfo; | ||
constructor(doc, html, fastbootInfo) { | ||
this.isDestroyed = false; | ||
this._doc = doc; | ||
this._html = html; | ||
this._fastbootInfo = fastbootInfo; | ||
this.applicationInstance = undefined; | ||
this.applicationInstanceInstance = undefined; | ||
} | ||
@@ -49,3 +52,9 @@ | ||
return insertIntoIndexHTML(this._html, this._htmlAttributes, this._head, this._body, this._bodyAttributes); | ||
return insertIntoIndexHTML( | ||
this._html, | ||
this._htmlAttributes, | ||
this._head, | ||
this._body, | ||
this._bodyAttributes | ||
); | ||
} | ||
@@ -65,3 +74,9 @@ | ||
chunks() { | ||
return insertIntoIndexHTML(this._html, this._htmlAttributes, this._head, this._body, this._bodyAttributes).then((html) => { | ||
return insertIntoIndexHTML( | ||
this._html, | ||
this._htmlAttributes, | ||
this._head, | ||
this._body, | ||
this._bodyAttributes | ||
).then(html => { | ||
let docParts = html.match(HTML_HEAD_REGEX); | ||
@@ -72,18 +87,15 @@ if (!docParts || docParts.length === 1) { | ||
let head = docParts[1]; | ||
let body = docParts[2]; | ||
let [, head, body] = docParts; | ||
if (!head || !body) { | ||
throw new Error('Could not idenfity head and body of the document! Make sure the document is well formed.'); | ||
throw new Error( | ||
'Could not idenfity head and body of the document! Make sure the document is well formed.' | ||
); | ||
} | ||
let chunks = [head]; | ||
let bodyParts = body.split(SHOEBOX_TAG_PATTERN); | ||
let plainBody = bodyParts[0]; | ||
chunks.push(plainBody); | ||
let [plainBody, ...shoeboxes] = body.split(SHOEBOX_TAG_PATTERN); | ||
let shoeboxes = bodyParts.splice(1); | ||
shoeboxes.forEach((shoebox) => { | ||
chunks.push(`${SHOEBOX_TAG_PATTERN}${shoebox}`); | ||
}); | ||
let chunks = [head, plainBody].concat( | ||
shoeboxes.map(shoebox => `${SHOEBOX_TAG_PATTERN}${shoebox}`) | ||
); | ||
@@ -102,3 +114,3 @@ return chunks; | ||
head: this._head, | ||
body: this._body | ||
body: this._body, | ||
}; | ||
@@ -110,10 +122,10 @@ } | ||
* | ||
* Called once the Result has finished being constructed and the application | ||
* instance has finished rendering. Once `finalize()` is called, state is | ||
* gathered from the completed application instance and statically copied | ||
* to this Result instance. | ||
* Called once the Result has finished being constructed and the | ||
* ApplicationInstance instance has finished rendering. Once `finalize()` is | ||
* called, state is gathered from the completed ApplicationInstance instance | ||
* and statically copied to this Result instance. | ||
*/ | ||
_finalize() { | ||
if (this.finalized) { | ||
throw new Error("Results cannot be finalized more than once"); | ||
throw new Error('Results cannot be finalized more than once'); | ||
} | ||
@@ -123,5 +135,5 @@ | ||
// and copy it to this Result object. | ||
let instance = this.instance; | ||
if (instance) { | ||
this._finalizeMetadata(instance); | ||
let { applicationInstanceInstance } = this; | ||
if (applicationInstanceInstance) { | ||
this._finalizeMetadata(applicationInstanceInstance); | ||
} | ||
@@ -135,8 +147,8 @@ | ||
_finalizeMetadata(instance) { | ||
if (instance._booted) { | ||
this.url = instance.getURL(); | ||
_finalizeMetadata(applicationInstanceInstance) { | ||
if (applicationInstanceInstance._booted) { | ||
this.url = applicationInstanceInstance.getURL(); | ||
} | ||
let response = this._fastbootInfo.response; | ||
let { response } = this._fastbootInfo; | ||
@@ -149,9 +161,18 @@ if (response) { | ||
_destroyAppInstance() { | ||
if (this.instance && !this._instanceDestroyed) { | ||
this._instanceDestroyed = true; | ||
this.instance.destroy(); | ||
return true; | ||
_destroy() { | ||
if (this.isDestroyed) { | ||
return false; | ||
} | ||
return false; | ||
this.isDestroyed = true; | ||
if (this.applicationInstanceInstance !== undefined) { | ||
this.applicationInstanceInstance.destroy(); | ||
} | ||
if (this.applicationInstance !== undefined) { | ||
this.applicationInstance.destroy(); | ||
} | ||
return true; | ||
} | ||
@@ -190,7 +211,11 @@ | ||
function missingTag(tag) { | ||
return Promise.reject(new Error(`Fastboot was not able to find ${tag} in base HTML. It could not replace the contents.`)); | ||
throw new Error( | ||
`Fastboot was not able to find ${tag} in base HTML. It could not replace the contents.` | ||
); | ||
} | ||
function insertIntoIndexHTML(html, htmlAttributes, head, body, bodyAttributes) { | ||
if (!html) { return Promise.resolve(html); } | ||
async function insertIntoIndexHTML(html, htmlAttributes, head, body, bodyAttributes) { | ||
if (!html) { | ||
return Promise.resolve(html); | ||
} | ||
let isBodyReplaced = false; | ||
@@ -223,11 +248,11 @@ let isHeadReplaced = false; | ||
if (head && !isHeadReplaced) { | ||
return missingTag('<!--EMBER_CLI_FASTBOOT_HEAD-->'); | ||
missingTag('<!--EMBER_CLI_FASTBOOT_HEAD-->'); | ||
} | ||
if (body && !isBodyReplaced) { | ||
return missingTag('<!--EMBER_CLI_FASTBOOT_BODY-->'); | ||
missingTag('<!--EMBER_CLI_FASTBOOT_BODY-->'); | ||
} | ||
return Promise.resolve(html); | ||
return html; | ||
} | ||
module.exports = Result; |
'use strict'; | ||
const chalk = require('chalk'); | ||
const vm = require('vm'); | ||
const sourceMapSupport = require('source-map-support'); | ||
class Sandbox { | ||
module.exports = class Sandbox { | ||
constructor(globals) { | ||
this.globals = globals; | ||
constructor(options) { | ||
this.globals = options.globals; | ||
this.sandbox = this.buildSandbox(); | ||
let sandbox = this.buildSandbox(); | ||
this.context = vm.createContext(sandbox); | ||
} | ||
buildSandbox() { | ||
var console = this.buildWrappedConsole(); | ||
var sourceMapSupport = require('./install-source-map-support'); | ||
var URL = require('url'); | ||
var globals = this.globals; | ||
let console = this.buildWrappedConsole(); | ||
let URL = require('url'); | ||
let globals = this.globals; | ||
var sandbox = { | ||
sourceMapSupport: sourceMapSupport, | ||
console: console, | ||
setTimeout: setTimeout, | ||
clearTimeout: clearTimeout, | ||
URL: URL, | ||
let sandbox = Object.assign( | ||
{ | ||
sourceMapSupport, | ||
console, | ||
setTimeout, | ||
clearTimeout, | ||
URL, | ||
// Convince jQuery not to assume it's in a browser | ||
module: { exports: {} } | ||
}; | ||
// Convince jQuery not to assume it's in a browser | ||
module: { exports: {} }, | ||
}, | ||
globals | ||
); | ||
for (var key in globals) { | ||
sandbox[key] = globals[key]; | ||
} | ||
// Set the global as `window`. | ||
@@ -41,7 +42,11 @@ sandbox.window = sandbox; | ||
buildWrappedConsole() { | ||
var wrappedConsole = Object.create(console); | ||
wrappedConsole.error = function() { | ||
console.error.apply(console, Array.prototype.map.call(arguments, function(a) { | ||
return typeof a === 'string' ? chalk.red(a) : a; | ||
})); | ||
let wrappedConsole = Object.create(console); | ||
wrappedConsole.error = function(...args) { | ||
console.error.apply( | ||
console, | ||
args.map(function(a) { | ||
return typeof a === 'string' ? chalk.red(a) : a; | ||
}) | ||
); | ||
}; | ||
@@ -51,4 +56,15 @@ | ||
} | ||
} | ||
module.exports = Sandbox; | ||
runScript(script) { | ||
script.runInContext(this.context); | ||
} | ||
eval(source, filePath) { | ||
var fileScript = new vm.Script(source, { filename: filePath }); | ||
fileScript.runInContext(this.context); | ||
} | ||
run(cb) { | ||
return cb.call(this.context, this.context); | ||
} | ||
}; |
@@ -12,2 +12,2 @@ 'use strict'; | ||
module.exports = getPackageName; | ||
module.exports = getPackageName; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 2 instances in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33536039
7
27
1165
17
1
136
15
- Removedrsvp@^4.8.0
- Removedrsvp@4.8.5(transitive)
Updatedchalk@^2.4.2
Updateddebug@^4.1.1
Updatednajax@^1.0.4
Updatedresolve@^1.12.0
Updatedsource-map-support@^0.5.13