From a45d5359518980e7587e72a31db1ff0dc64476cf Mon Sep 17 00:00:00 2001 From: Taranveer Virk Date: Thu, 1 Feb 2018 20:09:31 -0500 Subject: [PATCH] fixup! apply feedback --- packages/boot/src/boot.component.ts | 16 +++++-- packages/boot/src/booters/booter-utils.ts | 28 ++++++++--- .../boot/src/booters/controller.booter.ts | 12 ++--- .../src/{boot-strapper.ts => bootstrapper.ts} | 28 ++++++++--- packages/boot/src/index.ts | 2 +- .../controller.booter.app.acceptance.ts | 2 +- .../controller.booter.app.integration.ts | 2 +- .../boot/test/unit/boot.component.unit.ts | 6 +-- .../unit/booters/controller.booter.unit.ts | 46 +++++++++---------- ...-strapper.unit.ts => bootstrapper.unit.ts} | 6 +-- packages/cli/test/app.js | 9 ++-- packages/core/src/application.ts | 23 ++++++---- packages/core/src/booter.ts | 37 +++++++++++++-- packages/core/src/keys.ts | 2 +- packages/core/test/unit/application.test.ts | 6 +-- 15 files changed, 148 insertions(+), 77 deletions(-) rename packages/boot/src/{boot-strapper.ts => bootstrapper.ts} (72%) rename packages/boot/test/unit/{boot-strapper.unit.ts => bootstrapper.unit.ts} (96%) diff --git a/packages/boot/src/boot.component.ts b/packages/boot/src/boot.component.ts index 174cbb264ec2..89d9ba47a404 100644 --- a/packages/boot/src/boot.component.ts +++ b/packages/boot/src/boot.component.ts @@ -3,20 +3,30 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {BootStrapper} from './boot-strapper'; +import {Bootstrapper} from './bootstrapper'; import {Component, Application, CoreBindings} from '@loopback/core'; import {inject, BindingScope} from '@loopback/context'; import {ControllerBooter} from './booters'; +/** + * BootComponent is used to export the default list of Booter's made + * available by this module as well as bind the BootStrapper to the app so it + * can be used to run the Booters. + */ export class BootComponent implements Component { // Export a list of default booters in the component so they get bound // automatically when this component is mounted. booters = [ControllerBooter]; + /** + * + * @param app Application instance + */ constructor(@inject(CoreBindings.APPLICATION_INSTANCE) app: Application) { + // Bound as a SINGLETON so it can be cached as it has no state app - .bind(CoreBindings.BOOT_STRAPPER) - .toClass(BootStrapper) + .bind(CoreBindings.BOOTSTRAPPER) + .toClass(Bootstrapper) .inScope(BindingScope.SINGLETON); } } diff --git a/packages/boot/src/booters/booter-utils.ts b/packages/boot/src/booters/booter-utils.ts index df0dd79d590b..61884efe6f7c 100644 --- a/packages/boot/src/booters/booter-utils.ts +++ b/packages/boot/src/booters/booter-utils.ts @@ -15,6 +15,7 @@ const glob = promisify(require('glob')); * @param extensions An array of extensions to search for * @param nested A boolean to determine if nested folders in dirs should be searched * @param root Root folder to resolve dirs relative to + * @returns {string[]} Array of discovered files */ export async function discoverFiles( dirs: string[], @@ -34,6 +35,7 @@ export async function discoverFiles( * * @param pattern A glob pattern * @param root Root folder to start searching for matching files + * @returns {string[]} Array of discovered files */ export async function discoverFilesWithGlob( pattern: string, @@ -42,22 +44,36 @@ export async function discoverFilesWithGlob( return await glob(pattern, {root: root}); } +/** + * Given a function, returns true if it is a class, false otherwise. + * + * @param target The function to check if it's a class or not. + * @returns {boolean} True if target is a class. False otherwise. + */ +// tslint:disable-next-line:no-any +export function isClass(target: Constructor): boolean { + return ( + typeof target === 'function' && target.toString().indexOf('class') === 0 + ); +} + /** * Returns an Array of Classes from given files * * @param files An array of string of absolute file paths + * @returns {Promise>>} An array of Class Construtors from a file */ // tslint:disable-next-line:no-any -export async function loadClassesFromFiles(files: string[]): Promise { +export async function loadClassesFromFiles( + files: string[], + // tslint:disable-next-line:no-any +): Promise>> { // tslint:disable-next-line:no-any - const classes: Constructor[] = []; + const classes: Array> = []; files.forEach(file => { const ctrl = require(file); Object.keys(ctrl).forEach(cls => { - if ( - typeof ctrl[cls] === 'function' && - ctrl[cls].toString().indexOf('class') === 0 - ) { + if (isClass(ctrl[cls])) { classes.push(ctrl[cls]); } }); diff --git a/packages/boot/src/booters/controller.booter.ts b/packages/boot/src/booters/controller.booter.ts index 5f012d8ac024..7ade5b973979 100644 --- a/packages/boot/src/booters/controller.booter.ts +++ b/packages/boot/src/booters/controller.booter.ts @@ -19,6 +19,9 @@ import { * artifacts for LoopBack 4 Applications. * * It supports the following boot phases: config, discover, boot + * + * @param app Application instance + * @param bootConfig Config options for boot */ export class ControllerBooter implements Booter { options: ControllerOptions; @@ -27,11 +30,6 @@ export class ControllerBooter implements Booter { extensions: string[]; discovered: string[]; - /** - * - * @param app Application instance - * @param bootConfig Config options for boot - */ constructor( @inject(CoreBindings.APPLICATION_INSTANCE) public app: Application, @inject(BootBindings.BOOT_OPTIONS) public bootConfig: BootOptions, @@ -105,8 +103,8 @@ export class ControllerBooter implements Booter { * files in dirs. Defaults to ['.controller.js'] * @param nested Boolean to control if artifact discovery should check nested * folders or not. Default to true - * @param discovered An array of discovered files. This is set by the - * discover phase. + * @param glob A `glob` string to use when searching for files. This takes + * precendence over other options. */ export type ControllerOptions = { dirs: string | string[]; diff --git a/packages/boot/src/boot-strapper.ts b/packages/boot/src/bootstrapper.ts similarity index 72% rename from packages/boot/src/boot-strapper.ts rename to packages/boot/src/bootstrapper.ts index 16359639bb44..901a79bb3e17 100644 --- a/packages/boot/src/boot-strapper.ts +++ b/packages/boot/src/bootstrapper.ts @@ -24,7 +24,16 @@ import {BootBindings} from './keys'; import * as debugModule from 'debug'; const debug = debugModule('loopback:boot:bootstrapper'); -export class BootStrapper { +/** + * The Bootstrapper class provides the `boot` function that is responsible for + * finding and executing the Booters in an application based on given options. + * + * NOTE: Bootstrapper should be bound as a SINGLETON so it can be cached as + * it does not maintain any state of it's own. + * + * @param app Appliaction instance + */ +export class Bootstrapper { constructor( @inject(CoreBindings.APPLICATION_INSTANCE) private app: Application, ) {} @@ -35,6 +44,12 @@ export class BootStrapper { * complete before the next phase is started. * @param {BootOptions} bootOptions Options for boot. Bound for Booters to * receive via Dependency Injection. + * @param {Context} [ctx] Optional Context to use to resolve bindings. This is + * primarily useful when running app.boot() again but with different settings + * (in particular phases) such as 'start' / 'stop'. Using a returned Context from + * a previous boot call allows DI to retrieve the same instances of Booters previously + * used as they are bound using a CONTEXT scope. This is important as Booter instances + * may maintain state. */ async boot(bootOptions: BootOptions, ctx?: Context): Promise { if (!bootOptions.projectRoot) { @@ -58,7 +73,7 @@ export class BootStrapper { // and then resolving the bindings to get instances. const bindings = bootCtx.findByTag(CoreBindings.BOOTER_TAG); const booterInsts = await resolveList(bindings, binding => - Promise.resolve(bootCtx.get(binding.key)), + bootCtx.get(binding.key), ); // Determine the phases to be run. If a user set a phases filter, those @@ -79,14 +94,13 @@ export class BootStrapper { // Run phases of booters for (const phase of phases) { for (const inst of booterInsts) { - // Run phases if instance name is whitelisted. - // NOTE: Might need to polyfill .includes() - if (names.includes(inst.constructor.name)) { + const instName = inst.constructor.name; + if (names.includes(instName)) { if (inst[phase]) { await inst[phase](); - debug(`${inst.constructor.name} phase: ${phase} complete.`); + debug(`${instName} phase: ${phase} complete.`); } else { - debug(`${inst.constructor.name} phase: ${phase} not implemented.`); + debug(`${instName} phase: ${phase} not implemented.`); } } } diff --git a/packages/boot/src/index.ts b/packages/boot/src/index.ts index a6ee7e93cc81..c5e04c9299b5 100644 --- a/packages/boot/src/index.ts +++ b/packages/boot/src/index.ts @@ -4,6 +4,6 @@ // License text available at https://opensource.org/licenses/MIT export * from './booters'; -export * from './boot-strapper'; +export * from './bootstrapper'; export * from './boot.component'; export * from './keys'; diff --git a/packages/boot/test/acceptance/controller.booter.app.acceptance.ts b/packages/boot/test/acceptance/controller.booter.app.acceptance.ts index 43d32d251b4e..c7b23cb86f7e 100644 --- a/packages/boot/test/acceptance/controller.booter.app.acceptance.ts +++ b/packages/boot/test/acceptance/controller.booter.app.acceptance.ts @@ -10,7 +10,7 @@ import {resolve} from 'path'; describe('controller booter acceptance tests', () => { // tslint:disable-next-line:no-any let app: any; - const SANDBOX_PATH = resolve(__dirname, '../sandbox'); + const SANDBOX_PATH = resolve(__dirname, '../../.sandbox'); const sandbox = new TestSandbox(SANDBOX_PATH); beforeEach(resetSandbox); diff --git a/packages/boot/test/integration/controller.booter.app.integration.ts b/packages/boot/test/integration/controller.booter.app.integration.ts index b135849c2e07..5a1e004dfe60 100644 --- a/packages/boot/test/integration/controller.booter.app.integration.ts +++ b/packages/boot/test/integration/controller.booter.app.integration.ts @@ -10,7 +10,7 @@ import {ControllerBooter, ControllerDefaults} from '../../index'; import {resolve} from 'path'; describe('controller booter intengration tests', () => { - const SANDBOX_PATH = resolve(__dirname, '../sandbox'); + const SANDBOX_PATH = resolve(__dirname, '../../.sandbox'); const sandbox = new TestSandbox(SANDBOX_PATH); // tslint:disable-next-line:no-any diff --git a/packages/boot/test/unit/boot.component.unit.ts b/packages/boot/test/unit/boot.component.unit.ts index d947efb81f9d..7f1cccc00ff2 100644 --- a/packages/boot/test/unit/boot.component.unit.ts +++ b/packages/boot/test/unit/boot.component.unit.ts @@ -6,7 +6,7 @@ import {expect} from '@loopback/testlab'; import {Application, Booter, CoreBindings} from '@loopback/core'; import {Binding, Context} from '@loopback/context'; -import {BootComponent, BootBindings, BootStrapper} from '../../index'; +import {BootComponent, BootBindings, Bootstrapper} from '../../index'; describe('boot.component unit tests', () => { let app: Application; @@ -16,8 +16,8 @@ describe('boot.component unit tests', () => { beforeEach(getBootComponent); it('binds BootStrapper class', async () => { - const bootstrapper = await app.get(CoreBindings.BOOT_STRAPPER); - expect(bootstrapper).to.be.instanceOf(BootStrapper); + const bootstrapper = await app.get(CoreBindings.BOOTSTRAPPER); + expect(bootstrapper).to.be.instanceOf(Bootstrapper); }); function getApp() { diff --git a/packages/boot/test/unit/booters/controller.booter.unit.ts b/packages/boot/test/unit/booters/controller.booter.unit.ts index 76ec7e95ab49..aa836f585e76 100644 --- a/packages/boot/test/unit/booters/controller.booter.unit.ts +++ b/packages/boot/test/unit/booters/controller.booter.unit.ts @@ -9,7 +9,7 @@ import {ControllerBooter, ControllerDefaults} from '../../../index'; import {resolve, relative} from 'path'; describe('controller booter unit tests', () => { - const SANDBOX_PATH = resolve(__dirname, '../../sandbox'); + const SANDBOX_PATH = resolve(__dirname, '../../../.sandbox'); const sandbox = new TestSandbox(SANDBOX_PATH); let app: Application; @@ -92,11 +92,11 @@ describe('controller booter unit tests', () => { describe('ControllerBooter.discover()', () => { it('discovers correct files based on ControllerDefaults', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/nested/two.controller.js', ); const bootOptions: BootOptions = { @@ -118,7 +118,7 @@ describe('controller booter unit tests', () => { it('discovers correct files based on a glob pattern', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'ctrl/hello.ctrl.js', ); @@ -138,11 +138,11 @@ describe('controller booter unit tests', () => { it('discovers files without going into nested folders', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/nested/two.controller.js', ); const bootOptions: BootOptions = { @@ -163,11 +163,11 @@ describe('controller booter unit tests', () => { it('discovers files of specified extensions', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/two.ctrl.js', ); const bootOptions: BootOptions = { @@ -186,7 +186,7 @@ describe('controller booter unit tests', () => { it('discovers files in specified directory', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'ctrl/hello.controller.js', ); const bootOptions: BootOptions = { @@ -205,11 +205,11 @@ describe('controller booter unit tests', () => { it('discovers files of multiple extensions', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.ctrl.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/two.controller.js', ); const bootOptions: BootOptions = { @@ -231,11 +231,11 @@ describe('controller booter unit tests', () => { it('discovers files in multiple directories', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'ctrl/hello.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/two.controller.js', ); const bootOptions: BootOptions = { @@ -273,7 +273,7 @@ describe('controller booter unit tests', () => { it('discovers no files of an invalid extension', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/two.controller.js', ); const bootOptions: BootOptions = { @@ -309,7 +309,7 @@ describe('controller booter unit tests', () => { describe('ControllerBooter.load()', () => { it('binds a controller from discovered file', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.controller.js', ); const bootOptions: BootOptions = { @@ -331,19 +331,19 @@ describe('controller booter unit tests', () => { it('binds controllers from multiple files', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js'), + resolve(__dirname, '../../fixtures/hello.controller.js'), 'controllers/hello.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/hello.controller.js.map'), + resolve(__dirname, '../../fixtures/hello.controller.js.map'), 'controllers/hello.controller.js.map', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/nested/two.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js.map'), + resolve(__dirname, '../../fixtures/two.controller.js.map'), 'controllers/nested/two.controller.js.map', ); const bootOptions: BootOptions = { @@ -370,11 +370,11 @@ describe('controller booter unit tests', () => { it('binds multiple controllers from a file', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js'), + resolve(__dirname, '../../fixtures/two.controller.js'), 'controllers/two.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/two.controller.js.map'), + resolve(__dirname, '../../fixtures/two.controller.js.map'), 'controllers/two.controller.js.map', ); const bootOptions: BootOptions = { @@ -399,11 +399,11 @@ describe('controller booter unit tests', () => { it('does not throw on an empty file', async () => { await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/empty.controller.js'), + resolve(__dirname, '../../fixtures/empty.controller.js'), 'controllers/empty.controller.js', ); await sandbox.copyFile( - resolve(SANDBOX_PATH, '../fixtures/empty.controller.js.map'), + resolve(__dirname, '../../fixtures/empty.controller.js.map'), 'controllers/empty.controller.js.map', ); const bootOptions: BootOptions = { diff --git a/packages/boot/test/unit/boot-strapper.unit.ts b/packages/boot/test/unit/bootstrapper.unit.ts similarity index 96% rename from packages/boot/test/unit/boot-strapper.unit.ts rename to packages/boot/test/unit/bootstrapper.unit.ts index 7587cff793ee..7e5204944cd7 100644 --- a/packages/boot/test/unit/boot-strapper.unit.ts +++ b/packages/boot/test/unit/bootstrapper.unit.ts @@ -5,11 +5,11 @@ import {expect} from '@loopback/testlab'; import {Application, Booter, CoreBindings} from '@loopback/core'; -import {BootStrapper} from '../../index'; +import {Bootstrapper} from '../../index'; describe('boot-strapper unit tests', () => { let app: Application; - let bootstrapper: BootStrapper; + let bootstrapper: Bootstrapper; const booterKey = `${CoreBindings.BOOTER_PREFIX}.TestBooter`; const booterKey2 = `${CoreBindings.BOOTER_PREFIX}.TestBooter2`; @@ -72,7 +72,7 @@ describe('boot-strapper unit tests', () => { } function getBootStrapper() { - bootstrapper = new BootStrapper(app); + bootstrapper = new Bootstrapper(app); } class TestBooter implements Booter { diff --git a/packages/cli/test/app.js b/packages/cli/test/app.js index c33a8fd46a21..2440ebe08cef 100644 --- a/packages/cli/test/app.js +++ b/packages/cli/test/app.js @@ -27,11 +27,11 @@ describe('app-generator specfic files', () => { assert.file('src/application.ts'); assert.fileContent( 'src/application.ts', - /class MyAppApplication extends Application/ + /class MyAppApplication extends RestApplication/ ); assert.fileContent('src/application.ts', /constructor\(/); assert.fileContent('src/application.ts', /async start\(/); - assert.fileContent('src/application.ts', /RestComponent/); + assert.fileContent('src/application.ts', /BootComponent/); assert.file('src/index.ts'); assert.fileContent('src/index.ts', /new MyAppApplication/); @@ -48,7 +48,10 @@ describe('app-generator specfic files', () => { /@get\('\/ping'\)/ ); assert.fileContent('src/controllers/ping.controller.ts', /ping\(\)/); - assert.fileContent('src/controllers/ping.controller.ts', /\'\@loopback\/openapi\-v2\'/); + assert.fileContent( + 'src/controllers/ping.controller.ts', + /\'\@loopback\/openapi\-v2\'/ + ); assert.file; }); diff --git a/packages/core/src/application.ts b/packages/core/src/application.ts index 92e609cc3be5..f240e4ddea85 100644 --- a/packages/core/src/application.ts +++ b/packages/core/src/application.ts @@ -48,10 +48,10 @@ export class Application extends Context { /** * Register a controller class with this application. * - * @param controllerCtor {Function} The controller class - * (constructor function). + * @param {Function} controllerCtor The controller class + * (constructor function) * @param {string=} name Optional controller name, default to the class name - * @return {Binding} The newly created binding, you can use the reference to + * @returns {Binding} The newly created binding, you can use the reference to * further modify the binding, e.g. lock the value to prevent further * modifications. * @@ -71,10 +71,10 @@ export class Application extends Context { /** * Register a booter class / array of classes with this application. * - * @param booterCls {Function | Function[]} The booter class (constructor function). + * @param {Function | Function[]} booterCls The booter class (constructor function). * @param {string=} name Optional booter name, defaults to the class name. * Ignored is cls is an Array and the name defaults to the class name. - * @return {Binding | Binding[]} The newly created binding(s), you can use the + * @returns {Binding | Binding[]} The newly created binding(s), you can use the * reference to further modify the binding, e.g. lock the value to prevent * further modifications. * @@ -99,8 +99,9 @@ export class Application extends Context { /** * - * @param booterCls Constructor A Booter Class - * @param [name] Name the Booter Class should be bound to + * @param booterCls A Booter Class + * @param {string} name Name the Booter Class should be bound to + * @returns {Binding} The newly created Binding */ private _bindBooter( booterCls: Constructor, @@ -119,15 +120,17 @@ export class Application extends Context { * complete before the next phase is started. * @param {BootOptions} bootOptions Options for boot. Bound for Booters to * receive via Dependency Injection. + * @param {Context} [ctx] Optional context to use for running the bootstrapper. + * @returns {Promise} The Context used to run boot */ - async boot(bootOptions: BootOptions): Promise { + async boot(bootOptions: BootOptions, ctx?: Context): Promise { try { - const bootstrapper = await this.get(CoreBindings.BOOT_STRAPPER); + const bootstrapper = await this.get(CoreBindings.BOOTSTRAPPER); return await bootstrapper.boot(bootOptions); } catch (err) { throw new Error( `A Bootstrapper needs to be bound to ${ - CoreBindings.BOOT_STRAPPER + CoreBindings.BOOTSTRAPPER } to use app.boot()`, ); } diff --git a/packages/core/src/booter.ts b/packages/core/src/booter.ts index d01537ca2c34..418bbe1f2702 100644 --- a/packages/core/src/booter.ts +++ b/packages/core/src/booter.ts @@ -17,8 +17,17 @@ import {Constructor} from '@loopback/context'; * @interface Booter */ export interface Booter { + /** + * Configure phase of the Booter. It should set options / defaults in this phase. + */ configure?(): Promise; + /** + * Discover phase of the Booter. It should search for artifacts in this phase. + */ discover?(): Promise; + /** + * Load phase of the Booter. It should bind the artifacts in this phase. + */ load?(): Promise; } @@ -29,20 +38,38 @@ export interface Booter { export const BOOTER_PHASES = ['configure', 'discover', 'load']; /** - * Type Object for Options passed into .boot(). + * Type Object for Options passed into .boot() * - * projectRoot => Root of project. All other artifacts are resolved relative to this - * phases => An array of phases that should be executed. - * [prop: string]: any => Any options as defined by a Booter. + * @property projectRoot Root of project. All other artifacts are resolved relative to this + * @property booters An array of booters to bind to the application before running bootstrapper + * @property filter.booters An array of booters that should be run by the bootstrapper + * @property filter.phases An array of phases that should be run */ export type BootOptions = { + /** + * Root of the project. All other artifacts are resolved relative to this. + */ projectRoot: string; + /** + * Optional array of Booter Classes to bind to the application before running bootstrapper. + */ booters?: Constructor[]; - phases?: string[]; + /** + * Filter Object for Bootstrapper + */ filter?: { + /** + * Names of booters that should be run by Bootstrapper + */ booters?: string[]; + /** + * Names of phases that should be run by Bootstrapper + */ phases?: string[]; }; + /** + * Additional Properties + */ // tslint:disable-next-line:no-any [prop: string]: any; }; diff --git a/packages/core/src/keys.ts b/packages/core/src/keys.ts index ce5a5cf17b50..d31c9cb36ca4 100644 --- a/packages/core/src/keys.ts +++ b/packages/core/src/keys.ts @@ -28,7 +28,7 @@ export namespace CoreBindings { export const CONTROLLERS_TAG = 'controller'; // Key for Binding the BootStrapper Class - export const BOOT_STRAPPER = 'application.bootstrapper'; + export const BOOTSTRAPPER = 'application.bootstrapper'; export const BOOTER_TAG = 'booter'; export const BOOTER_PREFIX = 'booters'; diff --git a/packages/core/test/unit/application.test.ts b/packages/core/test/unit/application.test.ts index 0d60d779f7f1..2811ab6c869f 100644 --- a/packages/core/test/unit/application.test.ts +++ b/packages/core/test/unit/application.test.ts @@ -42,18 +42,18 @@ describe('Application', () => { it('throws an error if .boot() is called without a BootComponent bound', async () => { await expect(app.boot(bootOptions)).to.be.rejectedWith( `A Bootstrapper needs to be bound to ${ - CoreBindings.BOOT_STRAPPER + CoreBindings.BOOTSTRAPPER } to use app.boot()`, ); }); it('calls .boot() if a BootComponent is bound', async () => { app - .bind(CoreBindings.BOOT_STRAPPER) + .bind(CoreBindings.BOOTSTRAPPER) .toClass(FakeBootComponent) .inScope(BindingScope.SINGLETON); await app.boot(bootOptions); - const bootComponent = await app.get(CoreBindings.BOOT_STRAPPER); + const bootComponent = await app.get(CoreBindings.BOOTSTRAPPER); expect(bootComponent.bootCalled).to.be.True(); }); });