diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d393a12c6..8489266e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: - name: Yarn Install run: yarn install --ignore-engines --frozen-lockfile - name: Integration Tests - run: yarn workspace integration-tests test + run: yarn workspace integration-tests test test-packages: name: Test Packages diff --git a/package.json b/package.json index 1ed4fd5d3..e64370353 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,16 @@ "packages/*", "test-packages/*" ], + "scripts": { + "test": "npm-run-all test:*", + "test:ember-cli-fastboot": "yarn workspace ember-cli-fastboot test:ember", + "test:fastboot": "yarn workspace fastboot test", + "test:fastboot-express-middleware": "yarn workspace fastboot-express-middleware test", + "test:fastboot-app-server": "yarn workspace fastboot-app-server test:mocha", + "test:integration": "yarn workspace integration-tests test" + }, "devDependencies": { + "npm-run-all": "^4.1.5", "release-it": "^14.2.2", "release-it-lerna-changelog": "^3.1.0", "release-it-yarn-workspaces": "^2.0.0" diff --git a/packages/ember-cli-fastboot/addon/services/fastboot.js b/packages/ember-cli-fastboot/addon/services/fastboot.js index ec78ff9b5..5edc88982 100644 --- a/packages/ember-cli-fastboot/addon/services/fastboot.js +++ b/packages/ember-cli-fastboot/addon/services/fastboot.js @@ -78,6 +78,7 @@ const FastBootService = Service.extend({ let shoebox = Shoebox.create({ fastboot: this }); this.set('shoebox', shoebox); + this.set('_fastbootInfo', getOwner(this).lookup('info:-fastboot')); }, response: readOnly('_fastbootInfo.response'), diff --git a/packages/ember-cli-fastboot/lib/broccoli/fastboot-config.js b/packages/ember-cli-fastboot/lib/broccoli/fastboot-config.js index d66c80a29..bb629f67f 100644 --- a/packages/ember-cli-fastboot/lib/broccoli/fastboot-config.js +++ b/packages/ember-cli-fastboot/lib/broccoli/fastboot-config.js @@ -11,7 +11,7 @@ const Plugin = require('broccoli-plugin'); const stringify = require('json-stable-stringify'); -const LATEST_SCHEMA_VERSION = 3; +const LATEST_SCHEMA_VERSION = 6; module.exports = class FastBootConfig extends Plugin { constructor(inputNode, options) { diff --git a/packages/ember-cli-fastboot/tests/unit/services/fastboot/shoebox-test.js b/packages/ember-cli-fastboot/tests/unit/services/fastboot/shoebox-test.js index 120277422..f8e9ce6d3 100644 --- a/packages/ember-cli-fastboot/tests/unit/services/fastboot/shoebox-test.js +++ b/packages/ember-cli-fastboot/tests/unit/services/fastboot/shoebox-test.js @@ -58,35 +58,29 @@ module('Unit | Service | fastboot | shoebox', function(hooks) { }); test('retrieve returns undefined if isFastBoot true and shoebox is missing', function(assert) { + this.owner.register('info:-fastboot', {}, { instantiate: false }); let service = this.owner.factoryFor('service:fastboot').create({ - isFastBoot: true, - _fastbootInfo: {} + isFastBoot: true }); assert.strictEqual(service.get('shoebox').retrieve('foo'), undefined); }); test('retrieve returns undefined if isFastBoot true and key is missing', function(assert) { + this.owner.register('info:-fastboot', { shoebox: {} }, { instantiate: false }); let service = this.owner.factoryFor('service:fastboot').create({ - isFastBoot: true, - _fastbootInfo: { - shoebox: {} - } + isFastBoot: true }); assert.strictEqual(service.get('shoebox').retrieve('foo'), undefined); }); test('retrieve returns value if isFastBoot true and key is present', function(assert) { + this.owner.register('info:-fastboot', { shoebox: { foo: 'bar' } }, { instantiate: false }); let service = this.owner.factoryFor('service:fastboot').create({ - isFastBoot: true, - _fastbootInfo: { - shoebox: { - foo: 'bar' - } - } + isFastBoot: true }); assert.strictEqual(service.get('shoebox').retrieve('foo'), 'bar'); }); -}); \ No newline at end of file +}); diff --git a/packages/fastboot/src/ember-app.js b/packages/fastboot/src/ember-app.js index 3299a229a..266be8e8b 100644 --- a/packages/fastboot/src/ember-app.js +++ b/packages/fastboot/src/ember-app.js @@ -40,6 +40,7 @@ class EmberApp { this.appName = config.appName; this.html = config.html; this.sandboxRequire = config.sandboxRequire; + this.registerFastBootInfo = config.registerFastBootInfo; if (process.env.APP_CONFIG) { let appConfig = JSON.parse(process.env.APP_CONFIG); @@ -223,17 +224,17 @@ class EmberApp { * that promise for deferred rendering from the app. * * @param {string} path the URL path to render, like `/photos/1` - * @param {Object} fastbootInfo An object holding per request info * @param {Object} bootOptions An object containing the boot options that are used by * by ember to decide whether it needs to do rendering or not. * @param {Object} result + * @param {Object} result._fastbootInfo An object holding per request info * @param {Boolean} buildSandboxPerVisit if true, a new sandbox will * **always** be created, otherwise one * is created for the first request * only * @return {Promise} instance */ - async _visit(path, fastbootInfo, bootOptions, result, buildSandboxPerVisit) { + async _visit(path, bootOptions, result, buildSandboxPerVisit) { let shouldBuildApp = buildSandboxPerVisit || this._applicationInstance === undefined; let { app, isSandboxPreBuilt } = shouldBuildApp @@ -259,11 +260,11 @@ class EmberApp { let instance = await app.buildInstance(); result.applicationInstanceInstance = instance; - registerFastBootInfo(fastbootInfo, instance); + this.registerFastBootInfo(result._fastbootInfo, instance); await instance.boot(bootOptions); await instance.visit(path, bootOptions); - await fastbootInfo.deferredPromise; + await result._fastbootInfo.deferredPromise; } /** @@ -322,11 +323,11 @@ class EmberApp { } try { - await this._visit(path, fastbootInfo, bootOptions, result, buildSandboxPerVisit); + await this._visit(path, bootOptions, result, buildSandboxPerVisit); if (!disableShoebox) { // if shoebox is not disabled, then create the shoebox and send API data - createShoebox(doc, fastbootInfo); + createShoebox(doc, fastbootInfo.shoebox); } } catch (error) { // eslint-disable-next-line require-atomic-updates @@ -379,8 +380,7 @@ function buildBootOptions(shouldRender) { */ const hasOwnProperty = Object.prototype.hasOwnProperty; // jshint ignore:line -function createShoebox(doc, fastbootInfo) { - let shoebox = fastbootInfo.shoebox; +function createShoebox(doc, shoebox) { if (!shoebox) { return; } @@ -419,14 +419,6 @@ function escapeJSONString(string) { }); } -/* - * Builds a new FastBootInfo instance with the request and response and injects - * it into the application instance. - */ -function registerFastBootInfo(info, instance) { - info.register(instance); -} - function buildScripts(filePaths) { return filePaths.filter(Boolean).map(filePath => { let source = fs.readFileSync(filePath, { encoding: 'utf8' }); diff --git a/packages/fastboot/src/fastboot-info.js b/packages/fastboot/src/fastboot-info.js index 50b181be8..a045fcab3 100644 --- a/packages/fastboot/src/fastboot-info.js +++ b/packages/fastboot/src/fastboot-info.js @@ -30,14 +30,4 @@ module.exports = class FastBootInfo { deferRendering(promise) { this.deferredPromise = Promise.all([this.deferredPromise, promise]); } - - /* - * Registers this FastBootInfo instance in the registry of an Ember - * ApplicationInstance. It is configured to be injected into the FastBoot - * service, ensuring it is available inside instance initializers. - */ - register(instance) { - instance.register('info:-fastboot', this, { instantiate: false }); - instance.inject('service:fastboot', '_fastbootInfo', 'info:-fastboot'); - } }; diff --git a/packages/fastboot/src/fastboot-schema.js b/packages/fastboot/src/fastboot-schema.js index 91fcede9c..a71d5b574 100644 --- a/packages/fastboot/src/fastboot-schema.js +++ b/packages/fastboot/src/fastboot-schema.js @@ -23,12 +23,13 @@ const htmlEntrypoint = require('./html-entrypoint'); * should be added in fastboot lib) everytime fastboot addon schema version is bumped. */ const FastBootSchemaVersions = { - latest: 5, // latest schema version supported by fastboot library + latest: 6, // 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 htmlEntrypoint: 5, // schema version where we switch to loading the configuration directly from HTML + fastbootInfoLookup: 6, // schema version when `info:-fastboot` is looked up by `service:fastboot` instead of inject implicitly }; /** @@ -88,6 +89,10 @@ function loadConfig(distPath) { schemaVersion < FastBootSchemaVersions.strictWhitelist ); + let registerFastBootInfo = buildRegisterFastBootInfo( + schemaVersion < FastBootSchemaVersions.fastbootInfoLookup + ); + return { scripts, html, @@ -95,6 +100,21 @@ function loadConfig(distPath) { config, appName, sandboxRequire, + registerFastBootInfo, + }; +} + +/** + * Registers this FastBootInfo instance in the registry of an Ember + * ApplicationInstance. It can be later looked up by the fastboot service + */ +function buildRegisterFastBootInfo(isLegacySchema) { + return (info, instance) => { + instance.register('info:-fastboot', info, { instantiate: false }); + if (isLegacySchema) { + // In legacy fastboot versions, it is implicitly injected + instance.inject('service:fastboot', '_fastbootInfo', 'info:-fastboot'); + } }; }