UNPKG

8.35 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
3// Node module: @loopback/boot
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports._bindBooter = exports.bindBooter = exports.BootMixin = exports.Binding = void 0;
8const core_1 = require("@loopback/core");
9Object.defineProperty(exports, "Binding", { enumerable: true, get: function () { return core_1.Binding; } });
10const boot_component_1 = require("../boot.component");
11const component_application_booter_1 = require("../booters/component-application.booter");
12const keys_1 = require("../keys");
13/**
14 * Mixin for @loopback/boot. This Mixin provides the following:
15 * - Implements the Bootable Interface as follows.
16 * - Add a `projectRoot` property to the Class
17 * - Adds an optional `bootOptions` property to the Class that can be used to
18 * store the Booter conventions.
19 * - Adds the `BootComponent` to the Class (which binds the Bootstrapper and default Booters)
20 * - Provides the `boot()` convenience method to call Bootstrapper.boot()
21 * - Provides the `booter()` convenience method to bind a Booter(s) to the Application
22 * - Override `component()` to call `mountComponentBooters`
23 * - Adds `mountComponentBooters` which binds Booters to the application from `component.booters[]`
24 *
25 * @param superClass - Application class
26 * @returns A new class that extends the super class with boot related methods
27 *
28 * @typeParam T - Type of the application class as the target for the mixin
29 */
30function BootMixin(superClass) {
31 return class extends superClass {
32 // eslint-disable-next-line @typescript-eslint/no-explicit-any
33 constructor(...args) {
34 super(...args);
35 this.component(boot_component_1.BootComponent);
36 // We Dynamically bind the Project Root and Boot Options so these values can
37 // be used to resolve an instance of the Bootstrapper (as they are dependencies)
38 this.bind(keys_1.BootBindings.PROJECT_ROOT).toDynamicValue(() => this.projectRoot);
39 this.bind(keys_1.BootBindings.BOOT_OPTIONS).toDynamicValue(() => { var _a; return (_a = this.bootOptions) !== null && _a !== void 0 ? _a : {}; });
40 }
41 /**
42 * Override to detect and warn about starting without booting.
43 */
44 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
45 // @ts-ignore
46 async start() {
47 await super.start();
48 if (!this.booted) {
49 process.emitWarning('App started without booting. Did you forget to call ' +
50 '`await app.boot()`?', 'LoopBackWarning');
51 }
52 }
53 /**
54 * Convenience method to call bootstrapper.boot() by resolving bootstrapper
55 */
56 async boot() {
57 /* eslint-disable @typescript-eslint/ban-ts-comment */
58 // A workaround to access protected Application methods
59 const self = this;
60 if (this.state === 'booting') {
61 // @ts-ignore
62 return self.awaitState('booted');
63 }
64 // @ts-ignore
65 self.assertNotInProcess('boot');
66 // @ts-ignore
67 self.assertInStates('boot', 'created', 'booted');
68 if (this.state === 'booted')
69 return;
70 // @ts-ignore
71 self.setState('booting');
72 // Get a instance of the BootStrapper
73 const bootstrapper = await this.get(keys_1.BootBindings.BOOTSTRAPPER_KEY);
74 await bootstrapper.boot();
75 // @ts-ignore
76 this.setState('booted');
77 this.booted = true;
78 /* eslint-enable @typescript-eslint/ban-ts-comment */
79 }
80 /**
81 * Given a N number of Booter Classes, this method binds them using the
82 * prefix and tag expected by the Bootstrapper.
83 *
84 * @param booterCls - Booter classes to bind to the Application
85 *
86 * @example
87 * ```ts
88 * app.booters(MyBooter, MyOtherBooter)
89 * ```
90 */
91 booters(...booterCls) {
92 return booterCls.map(cls => bindBooter(this, cls));
93 }
94 /**
95 * Register a booter to boot a sub-application. See
96 * {@link createComponentApplicationBooterBinding} for more details.
97 *
98 * @param subApp - A sub-application with artifacts to be booted
99 * @param filter - A binding filter to select what bindings from the sub
100 * application should be added to the main application.
101 */
102 applicationBooter(subApp, filter) {
103 const binding = (0, component_application_booter_1.createComponentApplicationBooterBinding)(subApp, filter);
104 this.add(binding);
105 return binding;
106 }
107 /**
108 * Override to ensure any Booter's on a Component are also mounted.
109 *
110 * @param component - The component to add.
111 *
112 * @example
113 * ```ts
114 *
115 * export class ProductComponent {
116 * booters = [ControllerBooter, RepositoryBooter];
117 * providers = {
118 * [AUTHENTICATION_STRATEGY]: AuthStrategy,
119 * [AUTHORIZATION_ROLE]: Role,
120 * };
121 * };
122 *
123 * app.component(ProductComponent);
124 * ```
125 */
126 // Unfortunately, TypeScript does not allow overriding methods inherited
127 // from mapped types. https://github.com/microsoft/TypeScript/issues/38496
128 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
129 // @ts-ignore
130 component(componentCtor, nameOrOptions) {
131 const binding = super.component(componentCtor, nameOrOptions);
132 const instance = this.getSync(binding.key);
133 this.mountComponentBooters(instance);
134 return binding;
135 }
136 /**
137 * Get an instance of a component and mount all it's
138 * booters. This function is intended to be used internally
139 * by component()
140 *
141 * @param component - The component to mount booters of
142 */
143 mountComponentBooters(componentInstanceOrClass) {
144 const componentInstance = resolveComponentInstance(this);
145 if (componentInstance.booters) {
146 this.booters(...componentInstance.booters);
147 }
148 /**
149 * Determines if componentInstanceOrClass is an instance of a component,
150 * or a class that needs to be instantiated from context.
151 * @param ctx
152 */
153 function resolveComponentInstance(ctx) {
154 if (typeof componentInstanceOrClass !== 'function') {
155 return componentInstanceOrClass;
156 }
157 // TODO(semver-major) @bajtos: Reminder to remove this on the next major release
158 const componentName = componentInstanceOrClass.name;
159 const componentKey = `${core_1.CoreBindings.COMPONENTS}.${componentName}`;
160 return ctx.getSync(componentKey);
161 }
162 }
163 };
164}
165exports.BootMixin = BootMixin;
166/**
167 * Method which binds a given Booter to a given Context with the Prefix and
168 * Tags expected by the Bootstrapper
169 *
170 * @param ctx - The Context to bind the Booter Class
171 * @param booterCls - Booter class to be bound
172 */
173function bindBooter(ctx, booterCls) {
174 const binding = (0, core_1.createBindingFromClass)(booterCls, {
175 namespace: keys_1.BootBindings.BOOTERS,
176 defaultScope: core_1.BindingScope.SINGLETON,
177 }).tag(keys_1.BootTags.BOOTER);
178 ctx.add(binding);
179 /**
180 * Set up configuration binding as alias to `BootBindings.BOOT_OPTIONS`
181 * so that the booter can use `@config`.
182 */
183 if (binding.tagMap.artifactNamespace) {
184 ctx
185 .configure(binding.key)
186 .toAlias(`${keys_1.BootBindings.BOOT_OPTIONS.key}#${binding.tagMap.artifactNamespace}`);
187 }
188 return binding;
189}
190exports.bindBooter = bindBooter;
191// eslint-disable-next-line @typescript-eslint/naming-convention
192exports._bindBooter = bindBooter; // For backward-compatibility
193//# sourceMappingURL=boot.mixin.js.map
\No newline at end of file