esm/cjs 相关
打包成两种格式
打包成两种格式就是为了让自己的库有更好的兼容性,使用者可以根据情况来选择使用 esm 还是 cjs。那么如何配置发包配置呢? 在 package.json 文件中,"exports"、"module" 和 "main" 字段有着不同的作用。
"exports" 字段是在 Node.js 版本 12 及以上引入的,它用于指定模块的导出方式。导入模块时应该使用 cjs 还是 esm 取决于使用时的导入语法。 "module" 字段是在 Node.js 版本 8 及以上引入的,它用于指定 ES 模块的入口文件路径。在使用支持 ES 模块的环境中,例如现代浏览器或 Node.js 版本 13 及以上,这个字段可以用来指定默认的模块入口。 "main" 字段是 Node.js 中常用的字段,它用于指定 CommonJS 模块的入口文件路径。在使用 CommonJS 模块的环境中,例如 Node.js 版本 12 及以下,这个字段可以用来指定默认的模块入口。
如果包只支持 esm,那也可以用 main 来制定模块入口
{
"main": "lib/cjs/index.js",
"module": "lib/esm/index.mjs"
"typings": "lib/cjs/types/index.d.ts",
"exports": {
".": {
// 使用import语句,则types的入口在 /lib/esm/types/index.d.mts
// default 为项目入口文件
"import": {
"types": "./lib/esm/types/index.d.mts",
"default": "./lib/esm/index.mjs"
},
"require": {
"types": "./lib/cjs/types/index.d.ts",
"default": "./lib/cjs/index.js"
}
}
}
}
Error ERR_REQUIRE_ESM: require() of ES Module
Error ERR_REQUIRE_ESM: require() of ES Module /Users/teqi/Desktop/Projects/myCDNassets/node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/index.js from /Users/teqi/Desktop/Projects/myCDNassets/main.js not supported.
chalk 最新版采用 ESM 规范,但是 main.ts 被错误地编译成 CJS 规范的 main.js
解决办法:
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
},
node16/nodenext: TS 4.7 提出,为了更好的兼容 es module 和 commonjs 模块化,输入的模块取决于文件名(.cjs, .mjs/.cts, .mts) 或 package.json 中的 type 字段是("commonjs"、"module")
更改 module 通常需要更改 moduleResolution 配合,moduleResolution 是指 ts 文件中导入和导出的解析策略
当我们使用 node16 或者 nodenext 时,文件引入必须强制写后缀
https://nodejs.org/docs/latest-v16.x/api/esm.html#enabling
其他可选配置
-
node10(alias node): commonjs
-
bundler:ts5 新特性,结合第三方构建工具使用。 https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#moduleresolution-bundler
https://github.com/microsoft/TypeScript/pull/51669
{
"main": "main.js",
"type": "module",
"scripts": {
"start": "tsc && node ./dist/main.js"
}
}
注意这里如 果用 tsc main.ts 会读不到 tsconfig 的配置,导致还是使用了错误的规范
实在不行也可以考虑将包降级处理
在 esm 中使用 commonjs
https://nodejs.org/docs/latest-v16.x/api/esm.html#interoperability-with-commonjs
import statements
An import statement can reference an ES module or a CommonJS module.
import statements are permitted only in ES modules,
but dynamic import()
expressions are supported in CommonJS for loading ES modules.
可以在 cjs 中动态引入 esm 模块,使用 import()
在 esm 直接 import cjs 模块即可
When importing CommonJS modules, the module.exports object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.
require The CommonJS module require always treats the files it references as CommonJS.
Using require to load an ES module is not supported because ES modules have asynchronous execution. Instead, use import() to load an ES module from a CommonJS module.