1 | ;
|
2 | // Copyright IBM Corp. and LoopBack contributors 2018,2019. 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
|
6 | Object.defineProperty(exports, "__esModule", { value: true });
|
7 | exports.Bootstrapper = void 0;
|
8 | const tslib_1 = require("tslib");
|
9 | const core_1 = require("@loopback/core");
|
10 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
11 | const path_1 = require("path");
|
12 | const keys_1 = require("./keys");
|
13 | const mixins_1 = require("./mixins");
|
14 | const types_1 = require("./types");
|
15 | const debug = (0, debug_1.default)('loopback:boot:bootstrapper');
|
16 | /**
|
17 | * The Bootstrapper class provides the `boot` function that is responsible for
|
18 | * finding and executing the Booters in an application based on given options.
|
19 | *
|
20 | * NOTE: Bootstrapper should be bound as a SINGLETON so it can be cached as
|
21 | * it does not maintain any state of it's own.
|
22 | *
|
23 | * @param app - Application instance
|
24 | * @param projectRoot - The root directory of the project, relative to which all other paths are resolved
|
25 | * @param bootOptions - The BootOptions describing the conventions to be used by various Booters
|
26 | */
|
27 | let Bootstrapper = class Bootstrapper {
|
28 | constructor(app, projectRoot, bootOptions = {}) {
|
29 | this.app = app;
|
30 | this.projectRoot = projectRoot;
|
31 | this.bootOptions = bootOptions;
|
32 | // Resolve path to projectRoot and re-bind
|
33 | this.projectRoot = (0, path_1.resolve)(this.projectRoot);
|
34 | app.bind(keys_1.BootBindings.PROJECT_ROOT).to(this.projectRoot);
|
35 | // This is re-bound for testing reasons where this value may be passed directly
|
36 | // and needs to be propagated to the Booters via DI
|
37 | app.bind(keys_1.BootBindings.BOOT_OPTIONS).to(this.bootOptions);
|
38 | }
|
39 | /**
|
40 | * Function is responsible for calling all registered Booter classes that
|
41 | * are bound to the Application instance. Each phase of an instance must
|
42 | * complete before the next phase is started.
|
43 | *
|
44 | * @param execOptions - Execution options for boot. These
|
45 | * determine the phases and booters that are run.
|
46 | * @param ctx - Optional Context to use to resolve bindings. This is
|
47 | * primarily useful when running app.boot() again but with different settings
|
48 | * (in particular phases) such as 'start' / 'stop'. Using a returned Context from
|
49 | * a previous boot call allows DI to retrieve the same instances of Booters previously
|
50 | * used as they are bound using a CONTEXT scope. This is important as Booter instances
|
51 | * may maintain state.
|
52 | */
|
53 | async boot(execOptions, ctx) {
|
54 | var _a, _b, _c;
|
55 | const bootCtx = ctx !== null && ctx !== void 0 ? ctx : new core_1.Context(this.app);
|
56 | // Bind booters passed in as a part of BootOptions
|
57 | // We use _bindBooter so this Class can be used without the Mixin
|
58 | if (execOptions === null || execOptions === void 0 ? void 0 : execOptions.booters) {
|
59 | execOptions.booters.forEach(booter => (0, mixins_1.bindBooter)(this.app, booter));
|
60 | }
|
61 | // Determine the phases to be run. If a user set a phases filter, those
|
62 | // are selected otherwise we run the default phases (BOOTER_PHASES).
|
63 | const phases = (_b = (_a = execOptions === null || execOptions === void 0 ? void 0 : execOptions.filter) === null || _a === void 0 ? void 0 : _a.phases) !== null && _b !== void 0 ? _b : types_1.BOOTER_PHASES;
|
64 | // Find booters registered to the BOOTERS_TAG by getting the bindings
|
65 | const bindings = bootCtx.findByTag(keys_1.BootTags.BOOTER);
|
66 | // Prefix length. +1 because of `.` => 'booters.'
|
67 | const prefixLength = keys_1.BootBindings.BOOTERS.length + 1;
|
68 | // Names of all registered booters.
|
69 | const defaultBooterNames = bindings.map(binding => binding.key.slice(prefixLength));
|
70 | // Determining the booters to be run. If a user set a booters filter (class
|
71 | // names of booters that should be run), that is the value, otherwise it
|
72 | // is all the registered booters by default.
|
73 | const names = execOptions
|
74 | ? ((_c = execOptions.filter) === null || _c === void 0 ? void 0 : _c.booters)
|
75 | ? execOptions.filter.booters
|
76 | : defaultBooterNames
|
77 | : defaultBooterNames;
|
78 | // Filter bindings by names
|
79 | const filteredBindings = bindings.filter(binding => names.includes(binding.key.slice(prefixLength)));
|
80 | // Resolve Booter Instances
|
81 | const booterInsts = await (0, core_1.resolveList)(filteredBindings, binding =>
|
82 | // We cannot use Booter interface here because "filter.booters"
|
83 | // allows arbitrary string values, not only the phases defined
|
84 | // by Booter interface
|
85 | bootCtx.get(binding.key));
|
86 | // Run phases of booters
|
87 | for (const phase of phases) {
|
88 | for (const inst of booterInsts) {
|
89 | const instName = inst.constructor.name;
|
90 | if (inst[phase]) {
|
91 | debug(`${instName} phase: ${phase} starting.`);
|
92 | await inst[phase]();
|
93 | debug(`${instName} phase: ${phase} complete.`);
|
94 | }
|
95 | else {
|
96 | debug(`${instName} phase: ${phase} not implemented.`);
|
97 | }
|
98 | }
|
99 | }
|
100 | return bootCtx;
|
101 | }
|
102 | };
|
103 | Bootstrapper = tslib_1.__decorate([
|
104 | tslib_1.__param(0, (0, core_1.inject)(core_1.CoreBindings.APPLICATION_INSTANCE)),
|
105 | tslib_1.__param(1, (0, core_1.inject)(keys_1.BootBindings.PROJECT_ROOT)),
|
106 | tslib_1.__param(2, (0, core_1.inject)(keys_1.BootBindings.BOOT_OPTIONS, { optional: true })),
|
107 | tslib_1.__metadata("design:paramtypes", [Object, String, Object])
|
108 | ], Bootstrapper);
|
109 | exports.Bootstrapper = Bootstrapper;
|
110 | //# sourceMappingURL=bootstrapper.js.map |
\ | No newline at end of file |