如何判断特定模块是 CommonJS 模块还是 ES6 模块?

Aut*_*ant 6 javascript node.js node-modules typescript es6-modules

我有一个 Azure 函数(使用 NodeJS),我试图从中使用另一个团队发布的 npm 模块。在 npm 添加有问题的模块后,我尝试像这样使用它:

import * as Model from "@teo/app-model";
...
const appModel: Model.TheModel = new Model.TheModel([]);
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试运行 Azure 函数时,出现此错误:

[error] Worker was unable to load function pump: 'SyntaxError: Unexpected token export'
Run Code Online (Sandbox Code Playgroud)

我可以在网上找到的与该错误消息相关的结果表明,此错误通常表明我正在尝试使用 ES6 模块,这是 Node 不支持的(相反,我应该使用 CommonJS 模块)。挑战在于我认为该模块是一个 CommonJS 模块。生成模块的项目的 tsconfig.json 在 compilerOptions 中有这一行:

"module": "commonjs"
Run Code Online (Sandbox Code Playgroud)

为了验证或排除 ES6 vs CommonJS 问题,如果我查看 Azure Function 的 node_modules 目录中导入的模块,有没有办法通过查看它来判断它是 ES6 模块还是 CommonJS 模块?

谢谢!

JΛY*_*ÐΞV 12


\n

Node 中的模块与一个月前有很大不同,甚至比 18-20 个月前有很大不同。截至 2022 年 6 月,这是检查 TS-Node 包并推断包的模块类型的最新方法。

\n

检查的包裹可以是 , cjs,esm或两者 ( cjs& esm)

\n

现在可以使用 TypeScript 从一个 TS-Node 代码库(或项目,如果您愿意的话)构建 CJS 模块和 ESM 模块。事实上,如果您要维护这两种模块类型,则几乎需要有一个转译器(不幸的是,我无法进一步详细说明,因为这样的主题超出了原始问题的范围)。

\n
\n

应该注意的是: 了解如何从 TS-Node 包推断模块类型与了解如何将 TS-Node 包配置为不同的模块类型几乎相同。

\n
\n

TS-Node 包的模块类型可以从 2 个文件推断

\n
    \n
  1. package.json
  2. \n
  3. tsconfig.json
  4. \n
\n
package.json文件中
\n

\xc2\xa0 \xc2\xa0 ...该"type"属性是定义模块类型的位置。值得注意的是,这Node.js还将推断模块类型是什么,以及应该如何解析项目中的模块。TypeScript 在运行时不存在,其中 Node.js 是运行时环境。因为 Node.js 是 RTE,所以查看package.json并从中推断模块类型更为具体,但是,故事还远没有结束。 JavaScript 模块现在成为一个复杂的话题。

\n

在继续之前,让我们看一下package.jsonESM 配置。

\n
ESM 配置package.json
\n
// FILE: "./package.json"\n\n  {\n    "name": "foo-pkg-bar"\n    "version": "1.23.4",\n    "type": "module", // <-- You're Looking for this K/V pair\n    "license": "MIT",\n    "desc": "some description here...", \n    /* \n        ...rest of the JSON-file's properties... \n    */\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

当模块是ES-Moduleor ( ESM) 时,它的package.json属性将设置为"module"

\n

CommonJS Module( 或CJS Module) 中,配置不需要是明确的。这意味着您可以将该"type"属性完全排除在文件之外package.json,并且在这种非常常见的情况下,模块类型将默认为CJS. 该"type"属性可以配置为隐式声明模块类型CJS,在这种情况下,该"type"属性将如下所示:

\n
{\n   name: "foobar-foofoo-head",\n   version: 1.23.4,\n   type: "commonjs"\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

多个 Package.json 文件

\n

通常,在双模块类型包(为支持 ESM 和 CJS 环境而构建的包)中,可能会存在多个package.json文件。在双模块类型的包中,ESM构建将有一个package.json文件,其中添加了单个属性,如下所示。

\n
// package.json for the ESM build\n{\n  "type": "module"\n}\n
Run Code Online (Sandbox Code Playgroud)\n

CJS还将有自己的类型 package.json 文件,并且还将添加一个属性(见下文)。

\n
// package.json for the CJS build\n{\n  "type": "commonjs"\n}\n
Run Code Online (Sandbox Code Playgroud)\n

package.json 文件的其余部分将位于 ROOT-package.json 文件中。在这种情况下,文件结构将如下所示。

\n
\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cjs\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 project-stuff.d.ts\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 project-stuff.js\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cjs\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.d.cts\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 package.json        // <-- CJS "package.json"\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 esm\n\xe2\x94\x82       \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n\xe2\x94\x82       \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 project-stuff.d.ts\n\xe2\x94\x82       \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 project-stuff.js\n\xe2\x94\x82       \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.d.mts\n\xe2\x94\x82       \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.mjs\n\xe2\x94\x82       \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 package.json        // <-- ESM "package.json"\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package.json                // <-- ROOT "package.json"\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package-lock.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 project-stuff.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cts\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.mts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 tsconfig.base.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 tsconfig.cjs.json\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 tsconfig.esm.json\n\n\n
Run Code Online (Sandbox Code Playgroud)\n
上面的内容实际上来自我构建的项目,目的是自学如何转堆到两种模块类型。
\n

您可能注意到了很多tsconfig.json文件?

\n

老实说,如果您看到具有该命名约定的tsconfig 文件,您不需要进一步检查,它肯定可以用作CJS, 或ESM模块(假设它是一个工作包)。

\n

检查 TSConfig 中包的模块类型

\n

TSConfig 还可用于推断任何给定 TS/Node 项目的模块类型。打开 TSConfig,然后查找模块和模块解析属性。

\n
TS v4.7,简化了 Node/TS 项目中对 ESM 的支持
\n

tsconfig.json通过添加可传递给文件配置属性的有效值,简化了使用 TypeScript 转译的 Node 包中对 ESM 的"module"支持"moduleResolution"

\n
"module"& 模块分辨率现在可以接受新值
\n
    \n
  1. node12(没有顶级等待),
  2. \n
  3. node16(针对 LTS Node16 版本实施的 ESM 标准)&
  4. \n
  5. nodenext(最新 ESM 标准)
  6. \n
\n

如果您看到module&moduleResolution属性设置为这些值,则该项目正在被转换为 ES 模块。

\n
\n
CJS 和 ESM
\n

模块可以是 CJS 和 ESM。如果一个包被转换成 CJS 构建和 ESM 构建,您应该看到两个单独的构建目录。

\n

每个构建都应该有单独的package.json文件,利用package.json可以放置文件的级联文件结构(例如,树状层次结构)。

\n
    \n
  • 应该有一个基础package.json文件。
  • \n
  • 一个package.json文件的属性应"type"设置为module
  • \n
  • 而另一个package.json文件的属性应"type"设置为commonjs
  • \n
\n

这表明该软件包不仅支持ESM 和 CJS,而且同时是ESM 和 CJS

\n
\n
您可以再次通过检查tsconfig.json文件来推断模块类型。
\n

还会有多个 TSConfig 文件。关于 TS 配置文件的问题是,它们的命名或结构设置没有任何一种方式。据我所知,大多数人使用 a tsconfig.base,然后将其扩展到其他两个tsconfig.*.json文件。

\n

像这样的东西:

\n
    \n
  • 将会有 tsconfig.base.json 文件(我也看到过这个文件也被命名tsconfig-base.json
  • \n
  • 应该有一个CJStsconfig.*.json的文件
  • \n
  • 然后还应该有一个ESMtsconfig.*.json文件。
  • \n
\n

命名约定不是静态的,而是高度可配置和可定制的(这就是我们都喜欢 TS 的原因,对吗?)。对于某些人来说,习惯这一点可能很困难。起初这对我来说很难。

\n

关键是读取每个 TSConfig,并查看module&moduleResolution属性设置为什么。如果您看到一个 tsconfig 的模块属性设置为“commonjs”,而另一个 tsconfigtsconfigmodule属性设置为NodeNext,则意味着该项目会同时转换为ESM&CJS模块类型。

\n