如何在NestJS项目中正确导入map-obj库?
NestJS+TypeScript兼容ESM库map-obj的解决方案
问题背景
项目使用commonjs模块配置,导入纯ESM的map-obj库时触发以下错误:
/Users/alexs/Documents/projects/xml-controller-test/dist/app.controller.js:20 const map_obj_1 = require("map-obj"); ^ Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/alexs/Documents/projects/xml-controller-test/node_modules/map-obj/index.js from /Users/alexs/Documents/projects/xml-controller-test/dist/app.controller.js not supported. Instead change the require of index.js in /Users/alexs/Documents/projects/xml-controller-test/dist/app.controller.js to a dynamic import() which is available in all CommonJS modules. at Object.<anonymous> (/Users/alexs/Documents/projects/xml-controller-test/dist/app.controller.js:20:19)
尝试在package.json添加"type": "module"后,又出现CommonJS兼容错误:
ReferenceError: exports is not defined in ES module scope This file is being treated as an ES module because it has a '.js' file extension and '/Users/alexs/Documents/projects/xml-controller-test/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
解决方案
第一步:移除package.json中的"type": "module"
该配置会将项目所有.js文件视为ESM模块,但NestJS编译后的代码为CommonJS格式,使用了exports语法,因此会引发冲突,必须移除。
方案一:异步动态导入(无配置修改)
在需要使用map-obj的异步上下文(如生命周期钩子、请求处理方法)中使用动态导入,确保在调用前完成模块加载:
在服务中预加载模块
import { Injectable, OnModuleInit } from '@nestjs/common'; @Injectable() export class AppService implements OnModuleInit { private mapObject: typeof import('map-obj').default; private mapObjectSkip: typeof import('map-obj').mapObjectSkip; async onModuleInit() { const mapObjModule = await import('map-obj'); this.mapObject = mapObjModule.default; this.mapObjectSkip = mapObjModule.mapObjectSkip; } transformData(input: Record<string, any>) { return this.mapObject(input, (key, value) => { if (value === undefined) { return [key, this.mapObjectSkip]; } return [key.toLocaleUpperCase(), value]; }); } }
在控制器方法中直接导入
import { Controller, Post, Body } from '@nestjs/common'; @Controller('data') export class DataController { @Post('transform') async transform(@Body() input: Record<string, any>) { const { default: mapObject, mapObjectSkip } = await import('map-obj'); return mapObject(input, (key, value) => { return value === null ? [key, mapObjectSkip] : [key, value * 2]; }); } }
方案二:调整TypeScript配置(自动兼容ESM)
修改tsconfig.json的模块配置,让TypeScript自动处理CommonJS与ESM的交互:
{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", // 保留原有配置,如target、outDir、strict等 } }
此配置下,TypeScript会根据模块的类型自动生成对应的导入代码(ESM模块用动态import()),无需手动修改导入语句,原import mapObject, { mapObjectSkip } from 'map-obj';可正常使用。
注意:需确保NestJS版本在9.x及以上,该版本已支持
NodeNext模块配置。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

