我正在编写一个可以在 CommonJS 和 ESM 模块系统中使用的 JS 函数。
但是根据运行的情况,它会做不同的事情。
有什么方法可以在运行时代码中测试我们所处的系统吗?
我的方法使用单独的生成进程来检测文件正在运行的当前模块系统。
脚本作为子进程运行:
checkType.js
import('node:process').then(({ stdout }) => {
try {
require.main
} catch {
stdout.write('module')
return 'module'
}
try {
import.meta
} catch {
stdout.write('commonjs')
return 'commonjs'
}
})
用于确定运行时模块上下文的父进程:
moduleType.js
export const moduleType = async () => {
const { resolve } = await import('node:path')
const { spawnSync } = await import('node:child_process')
const { stdout, stderr } = spawnSync('node', [resolve(import.meta.dirname, 'checkType.js')])
let type = 'unknown'
// Only one of these will be non-falsy strings
const err = stderr.toString()
const out = stdout.toString()
/**
* Based on error messaging from v8
* @see https://github.com/nodejs/node/blob/bc13f23f7e25d750df9b0a7bfe891a3d69f995f3/deps/v8/src/common/message-template.h#L124
*/
if (/outside a module/i.test(err)) {
type = 'commonjs'
}
if (out) {
type = out
}
return type
}
moduleType.js 在构建过程中转换为 ESM 和 CJS 模块,这在 TypeScript 等其他工具链中可能很有用。
一些文件.ts
import { moduleType } from './moduleType.js'
;(async () => {
console.log(`ts file running in module mode ${await moduleType()}`
})()
模块类型由
"type"
中的 package.json
或文件扩展名(.cts
或 .mts
)决定。
这是否最终有用取决于除了发布双包之外是否存在任何用例。 FWIW,我在 npm 上的 node-module-type 下提供了此功能。
如果满足以下条件,那么您正在使用 CommonJS 模块运行:
typeof module !== 'undefined' && typeof exports !== 'undefined'
但是,要检查您是否正在使用 ECMAScript 模块,您可以使用以下条件:
typeof import !== 'undefined' && typeof export !== 'undefined'
您可以在代码中使用它,如下所示:
if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
// CommonJS code path
} else if (typeof import !== 'undefined' && typeof export !== 'undefined') {
// ESM code path
}
注意:我通常使用 TypeScript,这就是我使用的方法。当 TypeScript 编译为 JavaScript 时,它会改变形式。然而,我相信这是最好的解决方案,并且应该与常规普通 JS 完全兼容