Nest Commander自定义命令调用时导入服务找不到的问题排查
我有一个WebscraperModule模块,里面包含WebscraperService服务。在WebscraperService中导入了GeoCodingModule模块的GeoCodingService服务。我已经在GeoCodingModule里导出了GeoCodingService,也把GeoCodingModule导入到了WebscraperModule中,而且IDE里导入路径能正常跳转对应文件。但用Nest Commander创建的自定义命令调用WebscraperService时,报错:Error: Cannot find module 'src/@geoCoding/services/geoCoding.service'。运行npm run start:dev时完全正常,怀疑是Commander包配置问题,该怎么解决?
相关代码
Command 模块
@Module({ providers: [ WebscraperCommand ], imports: [ WebscraperModule ], exports: [ WebscraperCommand ] }) export class CommandsModule {}
Webscraper.command.ts
import { WebscraperService } from 'src/@webScraper/services/webscraper.service'; @Command({ name: 'basic', description: 'A parameter parse' }) export class WebscraperCommand extends CommandRunner { constructor(private readonly webscraperService: WebscraperService) { super(); }; async run() : Promise<void> { console.log("run") this.callMainScrapper(); }; @Option({ flags: '-msc', description: 'Call the main scraper', }) callMainScrapper(){ console.log("run"); this.webscraperService.mainScraper(); }; };
GeoCoding Module
@Global() @Module({ imports: [ HttpModule, ConfigModule.forRoot({ isGlobal: true }) ], providers: [ GeoCodingService ], exports: [ GeoCodingService ] }) export class GeoCodingModule {}
GeoCoding Service
@Injectable() export class GeoCodingService { constructor( private readonly _httpService: HttpService, private readonly _configService: ConfigService ) {}; public coordinates : object = {}; private _logger = new Logger('GeoCodingService'); private _apiKey : string = this._configService.get('API_KEY'); public async getGeoCoding(address: string): Promise<void> { const result : unknown = address.match(/^[A-Z]{1,2}[0-9]{1,2} ?[0-9][A-Z]{2}$/i); const url : string = `https://api.tomtom.com/search/2/geocode/${result[0].toUpperCase()}&limit=1.json?key=${this._apiKey}`; let res : object = {}; try { this._httpService.get(url).then(response => { if (response.data.results[0].position === undefined) { res = {}; } else { res = response.data.results[0].position; }; this.coordinates = res; }); } catch (error: any) { this._logger.error(error.message, error.stack); }; }; };
Webscraper Module
@Global() @Module({ imports: [ GeoCodingModule, HttpModule, ConfigModule.forRoot({ isGlobal: true }), ], exports: [ WebscraperService], providers: [WebscraperService, WebscraperCommand], }) export class WebscraperModule {};
Webscraper Service
import { GeoCodingService } from 'src/@geoCoding/services/geoCoding.service'; // 报错指向这一行 export class WebscraperService { constructor( private _geoCodingService: GeoCodingService, private readonly _httpService: HttpService, private readonly _configService: ConfigService ) {}; private async _getDataFromPage() : Promise<void> { for (const anchorLink of this._anchors) { const response : any = await fetch(anchorLink); const body : any = await response.text(); this._setPropertyType(anchorLink); this._setProps(body); const slice: Array<string> = this._addressData.slice(0, -2); const addressToString = slice.toString() // 这里调用getGeoCoding this._geoCodingService.getGeoCoding(addressToString) const property : PropertyInterface = { address: addressToString, telephone: this._telephoneNumber, website: this._websiteLink, canLeaveDogsUnattended: this._dogsUnattended, maxDogs: this._numberOfDogs, extraCharge: this._charge, coordinates: this._coordinates, type: this._type }; properties.push(property); console.log(property, "property") await this._sleep(1000); }; }; }
问题原因及解决办法
核心原因
Nest Commander执行命令时默认使用未编译的TS文件,而Node.js无法直接识别src/开头的绝对导入路径——这种路径通常靠TS配置的paths映射实现,但Node运行时不读取tsconfig.json的路径映射规则,导致模块查找失败。
解决步骤
确认TS路径映射配置
确保tsconfig.json里已经配置了paths映射src/路径,示例:{ "compilerOptions": { "baseUrl": "./", "paths": { "src/*": ["src/*"] } } }让Node识别TS路径映射
在命令启动脚本中添加tsconfig-paths/register参数,让Node加载TS的路径映射规则。修改package.json的命令:{ "scripts": { "command": "ts-node --require tsconfig-paths/register src/main.ts --basic" } }如果用Nest CLI的
nest start命令,需加上--exec参数:{ "scripts": { "command": "nest start --exec 'ts-node --require tsconfig-paths/register' -- --basic" } }移除重复的命令注册
你已经在CommandsModule中注册了WebscraperCommand,又在WebscraperModule的providers里重复添加,会导致重复实例化引发依赖问题。修改WebscraperModule:@Global() @Module({ imports: [ GeoCodingModule, HttpModule, ConfigModule.forRoot({ isGlobal: true }), ], exports: [ WebscraperService], providers: [WebscraperService], // 移除WebscraperCommand }) export class WebscraperModule {};避免全局模块重复初始化
ConfigModule.forRoot({ isGlobal: true })在GeoCodingModule和WebscraperModule都注册了,会导致重复初始化。建议只在根模块或单一模块中注册全局ConfigModule。
内容的提问来源于stack exchange,提问作者RandyBeans

