TypeScript+Node引入chalk报Must use import to load ES Module错误
问题背景
为优化错误日志的可视化效果与代码结构,引入仅支持Pure ESM规范的依赖包chalk。项目初始采用CommonJS规范,tsconfig.json与package.json均为CommonJS配置,package.json未声明"type": "module"。参照chalk官方文档、同类问题排障方案修改配置后,重启应用出现模块加载报错。
报错信息
[user] [user] > user@1.0.0 start [user] > ts-node-dev src/index.ts [user] [user] [INFO] 02:43:13 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.8.0, typescript ver. 4.7.2) [user] Compilation error in /app/src/index.ts [user] Error: Must use import to load ES Module: /app/src/index.ts [user] at Object.<anonymous> (/app/src/index.ts:1:7) [user] at Module._compile (node:internal/modules/cjs/loader:1105:14) [user] at Module._compile (/app/node_modules/source-map-support/source-map-support.js:568:25) [user] at Module.m._compile (/tmp/ts-node-dev-hook-07536074778072654.js:69:33) [user] at Module._extensions..js (node:internal/modules/cjs/loader:1159:10) [user] at require.extensions..jsx.require.extensions..js (/tmp/ts-node-dev-hook-07536074778072654.js:114:20) [user] at require.extensions.<computed> (/tmp/ts-node-dev-hook-07536074778072654.js:71:20) [user] at Object.nodeDevHook [as .ts] (/app/node_modules/ts-node-dev/lib/hook.js:63:13) [user] at Module.load (node:internal/modules/cjs/loader/981:32) [user] at Function.Module._load (node:internal/modules/cjs/loader/827:12) [user] [ERROR] 02:43:13 Error: Must use import to load ES Module: /app/src/index.ts
已调整的配置
package.json
{ "name": "user", "version": "1.0.0", "description": "", "type": "module", "main": "index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "ts-node-dev src/index.ts" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@types/bcrypt": "^5.0.0", "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.13", "@types/jsonwebtoken": "^8.5.8", "@types/morgan": "^1.9.3", "@types/multer": "^1.4.7", "axios": "^0.27.2", "bcrypt": "^5.0.1", "body-parser": "^1.20.0", "chalk": "^5.0.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dependencies": "^0.0.1", "dotenv": "^16.0.1", "express": "^4.18.1", "joi": "^17.6.0", "jsonwebtoken": "^8.5.1", "mongodb": "^4.6.0", "mongoose": "^6.3.3", "morgan": "^1.10.0", "multer": "^1.4.4", "nodemailer": "^6.7.5", "typescript": "^4.7.2", "uuid": "^8.3.2", "validator": "^13.7.0" }, "devDependencies": { "nodemon": "^2.0.16", "ts-node-dev": "^2.0.0" } }
入口文件 src/index.ts
import mongoose from "mongoose"; import app from "./app"; const dbURI ='blabla' */ let PORT = 3000; /* process.env.PORT */ async function start() { //T: Connecting to MongoDB Database try{ await mongoose .connect(dbURI) console.log(`connected to DB!`); }catch(err) { console.log(err); }; app.listen(PORT,()=>{ console.log( `connected to PORT: ${PORT} and listening...although it may not bee that port`) }); } start()
tsconfig.json
{ "compilerOptions": { /* Basic Options */ // "incremental": true, "target": "ESNext", "module": "ESNext", // "lib": [], // "allowJs": true, // "checkJs": true, // "jsx": "preserve", "declaration": true, // "declarationMap": true, // "sourceMap": true, // "outFile": "./", // "outDir": "./", // "rootDir": "./", // "composite": true, // "tsBuildInfoFile": "./", // "removeComments": true, // "noEmit": true, // "importHelpers": true, // "downlevelIteration": true, // "isolatedModules": true, /* Strict Type-Checking Options */ "strict": true, // "noImplicitAny": true, // "strictNullChecks": true, // "strictFunctionTypes": true, // "strictBindCallApply": true, // "strictPropertyInitialization": true, // "noImplicitThis": true, // "alwaysStrict": true, /* Additional Checks */ // "noUnusedLocals": true, // "noUnusedParameters": true, // "noImplicitReturns": true, // "noFallthroughCasesInSwitch": true, // "noUncheckedIndexedAccess": true, /* Module Resolution Options */ "moduleResolution": "node", // "baseUrl": "./", // "paths": {}, // "rootDirs": [], // "typeRoots": [], // "types": [], "allowSyntheticDefaultImports": true, "esModuleInterop": true, // "preserveSymlinks": true, // "allowUmdGlobalAccess": true, /* Source Map Options */ // "sourceRoot": "", // "mapRoot": "", // "inlineSourceMap": true, // "inlineSources": true, /* Experimental Options */ // "experimentalDecorators": true, // "emitDecoratorMetadata": true, /* Advanced Options */ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ "src/**/*" ] }
chalk测试业务文件(src/app.ts)
import express from "express"; import { NotFoundError } from "./errors/NotFoundError"; import errorHandler from "./middleware/error-handler"; import userIndex from "./routes/userIndex"; import morgan from "morgan" import cookieParser from "cookie-parser"; import chalk from 'chalk' const app = express(); app.use(express.json()); app.use(cookieParser()); app.set("trust proxy", true); const log = console.log; app.use( morgan( function (tokens, req, res) { return [ chalk.red (tokens.method(req, res)), chalk.green(tokens.url(req, res)), chalk.yellow(tokens.status(req, res),"-"), chalk.bgBlueBright(tokens["response-time"](req, res), "ms"), chalk.cyan(tokens['date'](req, res),'web'), chalk.blue(tokens.res(req, res, "content-length")), ].join(" "); }) ) log(chalk.red('I'm red')) log(chalk.green( 'I am a green line ' + chalk.blue.underline.bold('with a blue substring') + ' that becomes green again!' )); log(chalk.rgb(123, 45, 67).underline('Underlined reddish color')); app.use(morgan( function (tokens, req, res) { return chalk.yellow(tokens['date'](req, res),'web') + ' ' + chalk.blue(tokens['remote-addr'](req, res)) + ' ' + chalk.cyanBright(tokens.method(req, res)) + ' ' + chalk.green(tokens.url(req, res)) + ' ' + chalk.magentaBright(tokens.status(req, res)) + ' ' + chalk.red(tokens['response-time'](req, res)) })) app.use(morgan(` :remote-addr :remote-user :method :url :status :res[content-length] - :response-time ms :date[web] `)); app.use('/users', userIndex); app.all("*", async (req, res, next) => { next(new NotFoundError()); }); app.use(errorHandler); export default app ;
报错原因
项目配置为ESM规范后,ts-node-dev默认仍使用CommonJS的require钩子加载文件,Node检测到待加载文件是ESM格式但被CJS方式引用,直接抛出加载错误。同时现有代码还存在两处语法问题会阻塞启动。
解决方案
方案1:保留chalk v5 + 全项目ESM适配
- 修改package.json中的start启动命令,添加ESM模式参数:
"start": "ts-node-dev --esm src/index.ts" - 调整tsconfig.json的模块相关配置,适配Node ESM规范:
将compilerOptions下的"module": "ESNext"改为"module": "NodeNext","moduleResolution": "node"改为"moduleResolution": "NodeNext"。 - 修复现有代码的语法错误:
- 删除src/index.ts中
const dbURI ='blabla'后多余的*/注释闭合符 - 将src/app.ts中
log(chalk.red('I'm red'))改为log(chalk.red("I'm red")),解决单引号嵌套冲突
- 删除src/index.ts中
- 所有本地文件导入必须补全
.js后缀(Node ESM的强制要求),比如import app from "./app.js"、import { NotFoundError } from "./errors/NotFoundError.js",TS编译时会自动映射到对应的.ts文件。
方案2:降级chalk到v4,无需改动现有CJS配置(推荐)
chalk v4.x版本原生支持CommonJS,API和v5完全一致,不需要把整个项目改造成ESM,改造成本最低:
- 执行命令卸载现有chalk、安装v4版本:
npm uninstall chalk npm install chalk@4.1.2 - 删除package.json中的
"type": "module"字段 - 将tsconfig.json中
"module": "ESNext"改回"CommonJS" - 修复方案1中提到的两处代码语法错误,直接执行
npm start即可正常启动。
内容的提问来源于stack exchange,提问作者ASG4
相关产品推荐
相关产品推荐

