@loopback/boot
Version:
A collection of Booters for LoopBack 4 Applications
100 lines (90 loc) • 2.92 kB
text/typescript
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
// Node module: @loopback/boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {
BINDING_METADATA_KEY,
config,
Constructor,
CoreBindings,
hasInjections,
inject,
isDynamicValueProviderClass,
MetadataInspector,
} from '@loopback/core';
import {ApplicationWithServices} from '@loopback/service-proxy';
import debugFactory from 'debug';
import {BootBindings} from '../keys';
import {ArtifactOptions, booter} from '../types';
import {BaseArtifactBooter} from './base-artifact.booter';
const debug = debugFactory('loopback:boot:service-booter');
/**
* A class that extends BaseArtifactBooter to boot the 'Service' artifact type.
* Discovered services are bound using `app.service()`.
*
* Supported phases: configure, discover, load
*
* @param app - Application instance
* @param projectRoot - Root of User Project relative to which all paths are resolved
* @param bootConfig - Service Artifact Options Object
*/
('services')
export class ServiceBooter extends BaseArtifactBooter {
constructor(
public app: ApplicationWithServices,
(BootBindings.PROJECT_ROOT) projectRoot: string,
()
public serviceConfig: ArtifactOptions = {},
) {
(CoreBindings.APPLICATION_INSTANCE)
super(
projectRoot,
// Set Service Booter Options if passed in via bootConfig
Object.assign({}, ServiceDefaults, serviceConfig),
);
}
/**
* Uses super method to get a list of Artifact classes. Boot each file by
* creating a DataSourceConstructor and binding it to the application class.
*/
async load() {
await super.load();
for (const cls of this.classes) {
if (!isBindableClass(cls)) continue;
debug('Bind class: %s', cls.name);
const binding = this.app.service(cls);
debug('Binding created for class: %j', binding);
}
}
}
/**
* Default ArtifactOptions for DataSourceBooter.
*/
export const ServiceDefaults: ArtifactOptions = {
dirs: ['services'],
extensions: ['.service.js'],
nested: true,
};
function isServiceProvider(cls: Constructor<unknown>) {
const hasSupportedName = cls.name.endsWith('Provider');
const hasValueMethod = typeof cls.prototype.value === 'function';
return hasSupportedName && hasValueMethod;
}
function isBindableClass(cls: Constructor<unknown>) {
if (MetadataInspector.getClassMetadata(BINDING_METADATA_KEY, cls)) {
return true;
}
if (hasInjections(cls)) {
return true;
}
if (isServiceProvider(cls)) {
debug('Provider class found: %s', cls.name);
return true;
}
if (isDynamicValueProviderClass(cls)) {
debug('Dynamic value provider class found: %s', cls.name);
return true;
}
debug('Skip class not decorated with @injectable: %s', cls.name);
return false;
}