#node.js #dependency-injection #nestjs #metaprogramming
Вопрос:
Моя команда пытается определить способ динамического DI таким образом, чтобы можно было выполнять массовую вставку без необходимости указывать импортировать каждый модуль.
@Module({
...
providers: [
WorkerService,
WorkerResolver,
Worker2Service,
Worker2Resolver........
]
})
хотите достичь
var allModules = ... // logic here to include all my resolvers, or all my services
@Module({
...
providers: [
...allModules
]
})
Комментарии:
1. Единственный простой способ, который я могу придумать для регистрации каждого поставщика , — это чтение всех файлов, которые экспортирует поставщик (используя соглашение по сравнению с моделью конфигурации ), которую вы хотите зарегистрировать
2. @MicaelLevi Да, вот где мы застряли, DI происходит во время компиляции, и если я попытаюсь прочитать файлы, он сообщит, что в каталоге dist/ ничего не существует.
3. Я думаю, что вы можете обойти это, используя динамические модули
Ответ №1:
Вы можете использовать пакет glob для динамического поиска модулей, а затем использовать функцию динамического модуля NestJS для их динамической загрузки.
Предположим, что все ваши рабочие файлы хранятся в каталоге с именем workers
и расширением .worker.ts
:
@Module({})
export class WorkerModule {
static forRootAsync(): DynamicModule {
return {
module: WorkerModule ,
imports: [WorkerCoreModule.forRootAsync()],
};
}
}
export class WorkerCoreModule {
static async forRootAsync(): Promise<DynamicModule> {
// Feel free to change path if your structure is different
const workersPath = glob.sync('src/**/workers/*.worker.ts');
const workersRelativePathWithoutExt = modelsPath
// Replace src, because you are probably running the code
// from dist folder
.map((path) => path.replace('src/', './../'))
.map((path) => path.replace('.ts', ''));
const workerProviders: Provider<any>[] = [];
const importedModules = await Promise.all(
workersRelativePathWithoutExt.map((path) => import(path)),
);
importedModules.forEach((modules) => {
// Might be different if you are using default export instead
const worker = modules[Object.keys(modules)[0]];
workerProviders.push({
provide: worker.name,
useValue: worker,
});
});
return {
module: WorkerCoreModule,
providers: [...workerProviders],
// You can omit exports if providers are meant to be used
// only in this module
exports: [...workerProviders],
};
}
}
Теперь предположим , что у вас есть простой рабочий класс с путем src/anyModule/workers/simple-worker.ts
, вы можете использовать его следующим образом:
class WrokersService {
constructor(@Inject('SimpleWorker') simpleWroker: SimpleWorker) {}
.
.
.
}
Если вы хотите опустить @Inject('SimpleWorker')
и автоматически вводить модули, такие как службы NestJS, вам необходимо внести эти изменения в WorkerCoreModule
:
workerProviders.push({
provide: worker,
});
Однако для того, чтобы это сработало, вам нужно быть уверенным, что ваши рабочие классы украшены @injectable()
.
Комментарии:
1. Я думаю, что для этого вам все равно нужно внедрить SimpleWorker в WorkerService. Кроме того, если я правильно понимаю ваш подход, он должен предполагать, что все файлы преобразованы 1 в 1 из typescript в javscript. Когда на самом деле наше приложение объединено в один файл. Таким образом, решение должно быть найдено во время компиляции.
2. Вы используете Webpack? Как это оказывается в одном файле?
3. да, мы собираем его в main.js аналогично клиентским приложениям