bootstrap.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { INestApplication } from '@nestjs/common';
  2. import { NestFactory } from '@nestjs/core';
  3. import { EntitySubscriberInterface } from 'typeorm';
  4. import { Type } from '../../shared/shared-types';
  5. import { InternalServerError } from './common/error/errors';
  6. import { ReadOnlyRequired } from './common/types/common-types';
  7. import { getConfig, setConfig } from './config/config-helpers';
  8. import { VendureConfig } from './config/vendure-config';
  9. import { registerCustomEntityFields } from './entity/custom-entity-fields';
  10. export type VendureBootstrapFunction = (config: VendureConfig) => Promise<INestApplication>;
  11. /**
  12. * Bootstrap the Vendure server.
  13. */
  14. export async function bootstrap(userConfig: Partial<VendureConfig>): Promise<INestApplication> {
  15. const config = await preBootstrapConfig(userConfig);
  16. // The AppModule *must* be loaded only after the entities have been set in the
  17. // config, so that they are available when the AppModule decorator is evaluated.
  18. // tslint:disable-next-line:whitespace
  19. const appModule = await import('./app.module');
  20. const app = await NestFactory.create(appModule.AppModule, {
  21. cors: config.cors,
  22. logger: config.silent ? false : undefined,
  23. });
  24. await runPluginOnBootstrapMethods(config, app);
  25. await app.listen(config.port);
  26. return app;
  27. }
  28. /**
  29. * Setting the global config must be done prior to loading the AppModule.
  30. */
  31. export async function preBootstrapConfig(
  32. userConfig: Partial<VendureConfig>,
  33. ): Promise<ReadOnlyRequired<VendureConfig>> {
  34. if (userConfig) {
  35. setConfig(userConfig);
  36. }
  37. // Entities *must* be loaded after the user config is set in order for the
  38. // base VendureEntity to be correctly configured with the primary key type
  39. // specified in the EntityIdStrategy.
  40. // tslint:disable-next-line:whitespace
  41. const pluginEntities = getEntitiesFromPlugins(userConfig);
  42. const entities = await getAllEntities(userConfig);
  43. const { coreSubscribersMap } = await import('./entity/subscribers');
  44. setConfig({
  45. dbConnectionOptions: {
  46. entities,
  47. subscribers: Object.values(coreSubscribersMap) as Array<Type<EntitySubscriberInterface>>,
  48. },
  49. });
  50. let config = getConfig();
  51. config = await runPluginConfigurations(config);
  52. registerCustomEntityFields(config);
  53. return config;
  54. }
  55. /**
  56. * Initialize any configured plugins.
  57. */
  58. async function runPluginConfigurations(
  59. config: ReadOnlyRequired<VendureConfig>,
  60. ): Promise<ReadOnlyRequired<VendureConfig>> {
  61. for (const plugin of config.plugins) {
  62. config = (await plugin.configure(config)) as ReadOnlyRequired<VendureConfig>;
  63. }
  64. return config;
  65. }
  66. /**
  67. * Run the onBootstrap() method of any configured plugins.
  68. */
  69. async function runPluginOnBootstrapMethods(
  70. config: ReadOnlyRequired<VendureConfig>,
  71. app: INestApplication,
  72. ): Promise<void> {
  73. function inject<T>(type: Type<T>): T {
  74. return app.get(type);
  75. }
  76. for (const plugin of config.plugins) {
  77. if (plugin.onBootstrap) {
  78. await plugin.onBootstrap(inject);
  79. }
  80. }
  81. }
  82. /**
  83. * Returns an array of core entities and any additional entities defined in plugins.
  84. */
  85. async function getAllEntities(userConfig: Partial<VendureConfig>): Promise<Array<Type<any>>> {
  86. const { coreEntitiesMap } = await import('./entity/entities');
  87. const coreEntities = Object.values(coreEntitiesMap) as Array<Type<any>>;
  88. const pluginEntities = getEntitiesFromPlugins(userConfig);
  89. const allEntities: Array<Type<any>> = coreEntities;
  90. // Check to ensure that no plugins are defining entities with names
  91. // which conflict with existing entities.
  92. for (const pluginEntity of pluginEntities) {
  93. if (allEntities.find(e => e.name === pluginEntity.name)) {
  94. throw new InternalServerError(`error.entity-name-conflict`, { entityName: pluginEntity.name });
  95. } else {
  96. allEntities.push(pluginEntity);
  97. }
  98. }
  99. return [...coreEntities, ...pluginEntities];
  100. }
  101. /**
  102. * Collects all entities defined in plugins into a single array.
  103. */
  104. function getEntitiesFromPlugins(userConfig: Partial<VendureConfig>): Array<Type<any>> {
  105. if (!userConfig.plugins) {
  106. return [];
  107. }
  108. return userConfig.plugins
  109. .map(p => (p.defineEntities ? p.defineEntities() : []))
  110. .reduce((all, entities) => [...all, ...entities], []);
  111. }