UNPKG

3.92 kBPlain TextView Raw
1// Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
2// Node module: @loopback/boot
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6import {
7 Application,
8 Binding,
9 BindingFilter,
10 Constructor,
11 CoreBindings,
12 createBindingFromClass,
13 inject,
14} from '@loopback/core';
15import debugFactory from 'debug';
16import {BootBindings} from '../keys';
17import {Bootable, Booter, booter} from '../types';
18
19const debug = debugFactory('loopback:boot:booter:component-application');
20
21/**
22 * Binding keys excluded from a sub application. These bindings booted from the
23 * sub application won't be added to the main application.
24 */
25export const bindingKeysExcludedFromSubApp = [
26 BootBindings.BOOT_OPTIONS.key,
27 BootBindings.PROJECT_ROOT.key,
28 BootBindings.BOOTSTRAPPER_KEY.key,
29 CoreBindings.APPLICATION_CONFIG.key,
30 CoreBindings.APPLICATION_INSTANCE.key,
31 CoreBindings.APPLICATION_METADATA.key,
32 CoreBindings.LIFE_CYCLE_OBSERVER_REGISTRY.key,
33 CoreBindings.LIFE_CYCLE_OBSERVER_OPTIONS.key,
34];
35
36/**
37 * Create a booter that boots the component application. Bindings that exist
38 * in the component application before `boot` are skipped. Locked bindings in
39 * the main application will not be overridden.
40 *
41 * @param componentApp - The application exposing a component
42 * @param filter Binding filter to selected bindings to be added
43 */
44export function createBooterForComponentApplication(
45 componentApp: Application & Bootable,
46 filter: BindingFilter = () => true,
47): Constructor<Booter> {
48 /**
49 * A booter to boot artifacts for the component application
50 */
51 @booter('componentApplications')
52 class ComponentApplicationBooter implements Booter {
53 constructor(
54 @inject(CoreBindings.APPLICATION_INSTANCE) private mainApp: Application,
55 ) {}
56
57 async load() {
58 /**
59 * List all bindings before boot
60 */
61 let bindings = componentApp.find(() => true);
62 const bindingsBeforeBoot = new Set(bindings);
63 // Boot the component application
64 await componentApp.boot();
65 /**
66 * Add bindings from the component application to the main application
67 */
68 bindings = componentApp.find(filter);
69 for (const binding of bindings) {
70 // Exclude boot related bindings
71 if (bindingKeysExcludedFromSubApp.includes(binding.key)) continue;
72
73 // Exclude bindings from the app before boot
74 if (bindingsBeforeBoot.has(binding)) {
75 debug(
76 'Skipping binding %s that exists before booting %s',
77 binding.key,
78 componentApp.name,
79 );
80 continue;
81 }
82
83 // Do not override locked bindings
84 const locked = this.mainApp.find(binding.key).some(b => b.isLocked);
85 if (locked) {
86 debug(
87 'Skipping binding %s from %s - locked in %s',
88 binding.key,
89 componentApp.name,
90 this.mainApp.name,
91 );
92 continue;
93 }
94
95 debug(
96 'Adding binding from %s to %s',
97 componentApp.name,
98 this.mainApp.name,
99 binding,
100 );
101 this.mainApp.add(binding as Binding);
102 }
103 }
104 }
105 return ComponentApplicationBooter;
106}
107
108/**
109 * Create a binding to register a booter that boots the component application.
110 * Bindings that exist in the component application before `boot` are skipped.
111 * Locked bindings in the main application will not be overridden.
112 *
113 * @param componentApp - The application exposing a component
114 * @param filter Binding filter to selected bindings to be added
115 */
116export function createComponentApplicationBooterBinding(
117 componentApp: Application & Bootable,
118 filter?: BindingFilter,
119) {
120 return createBindingFromClass(
121 createBooterForComponentApplication(componentApp, filter),
122 {key: `booters.${componentApp.name}`},
123 );
124}