Dynamic import of module and loading of a dynamic module ends in "metatype is not a constructor" error
See original GitHub issueBug Report
As stated in #3277 I’m actually trying to get a plugin architecture working. For this I used the dynamic modules of NestJS and a simple import(), but in fact, that is not working as expected.
I have two modules:
- App-Module (for the core app)
- Plugin-Module (which loads all available plugins)
The plugins are your libraries which I have built before. But If i want to dynamically load the plugin-modulewith all child-modules (plugins) the compiler says:
[Nest] 14908 - 2019-11-01 10:12:32 AM [loadPlugins] Loading plugins from C:\Users\mfranke\Documents\GitHub\cre8ive\cre8ive-server\plugins [Nest] 14908 - 2019-11-01 10:12:48 AM [NestFactory] Starting Nest application… +15595ms All modules resolved: 1 plugins [Nest] 14908 - 2019-11-01 10:12:48 AM [ExceptionHandler] metatype is not a constructor +40ms TypeError: metatype is not a constructor at Injector.instantiateClass (C:\Users\mfranke\Documents\GitHub\cre8ive\cre8ive-server\node_modules@nestjs\core\injector\injector.js:276:19) at callback (C:\Users\mfranke\Documents\GitHub\cre8ive\cre8ive-server\node_modules@nestjs\core\injector\injector.js:74:41) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:834:11) at startup (internal/bootstrap/node.js:283:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3) Waiting for the debugger to disconnect…
Input Code
Code of my app-module:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PluginModule } from './plugin/plugin.module';
@Module({
imports: [PluginModule.registerPluginsAsync()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
Code of plugin-module:
import { PluginAModule } from '@c8-plugin/plugin-a';
import { DynamicModule, Logger, Module } from '@nestjs/common';
import * as fs from 'fs';
import * as path from 'path';
import { PluginController } from './plugin/plugin.controller';
export const PLUGIN_PATH = path.normalize(path.join(process.cwd(), 'plugins'));
@Module({
controllers: [PluginController],
})
export class PluginModule {
public static async registerPluginsAsync(): Promise<DynamicModule> {
return this.loadPlugins();
}
private static loadPlugins(): Promise<DynamicModule> {
Logger.log(`Loading plugins from ${PLUGIN_PATH}`, 'loadPlugins');
const loadedPlugins: Array<Promise<DynamicModule>> = new Array();
this.searchPluginsInFolder(PLUGIN_PATH).forEach(filePath => {
loadedPlugins.push(
this.loadPlugin(filePath).then(module => {
if (module) { return module; }
}),
);
});
return Promise.all(loadedPlugins).then((allPlugins: DynamicModule[]) => {
console.log('All modules resolved: ', allPlugins.length, 'plugins');
return {
module: PluginModule,
imports: [...allPlugins],
} as Dynamic
});
}
private static loadPlugin(path: string): Promise<DynamicModule> {
const modulePlugin = import(path);
return modulePlugin;
}
private static searchPluginsInFolder(folder: string): string[] {
return this.recFindByExt(folder, 'mjs');
}
private static recFindByExt(base: string, ext: string, files?, result?): any[] {
files = files || fs.readdirSync(base);
result = result || [];
files.forEach((file) => {
const newbase = path.join(base, file);
if (fs.statSync(newbase).isDirectory()) {
result = this.recFindByExt(newbase, ext, fs.readdirSync(newbase), result);
} else {
if (file.substr(-1 * (ext.length + 1)) === '.' + ext) {
result.push(newbase);
}
}
},
);
return result;
}
}
If I change the return of the DynamicModule from
return {
module: PluginModule,
imports: [...allPlugins],
} as Dynamic
to
return {
module: PluginModule,
imports: [PluginAModule],
} as Dynamic
The plugin is a simple library created from the cli. It has only one controller which onyl returns a simple string, to test if the module injection works.
it works flawlessly.
Expected behavior
I want to load modules at start of the application from a given folder, which are libs of NestJS.
Environment
Nest version: 6.10.4 For Tooling issues:
- Node version: v10.16.3
- Platform: Windows
Issue Analytics
- State:
- Created 4 years ago
- Comments:9 (2 by maintainers)
Top Related StackOverflow Question
I tried messaging you on discord, but haven’t heard back from you. I need a little help getting the set up done correctly
Yes, there is a repo with this @jmcdo29 https://github.com/Disane87/nestjs-dynamic-plugin/tree/dev
Just clone it and execute it with vscode or somthing else. In
plugins/you find a plugin which is build with a lib of nestjs (found inlibs/)