为什么 TypeScript 导入 CommonJS 而不是 ESM

HUI*_*ANG 7 commonjs node.js typescript es6-modules

我按照本教程创建了一个双包。包文件结构如下所示:

my-awesome-lib
  package.json
  dist
  |-- mjs
    |-- package.json
    |-- index.js
  |-- cjs
    |-- package.json
    |-- index.js
Run Code Online (Sandbox Code Playgroud)

package.json

  "main": "dist/cjs/index.js",
  "module": "dist/mjs/index.js",
  "exports": {
    ".": {
      "require": "./dist/cjs/index.js",
      "import": "./dist/mjs/index.js"
    }
  },
Run Code Online (Sandbox Code Playgroud)

文件夹package.json中的mjs

{
    "type": "module"
}
Run Code Online (Sandbox Code Playgroud)

文件夹package.json中的cjs

{
    "type": "commonjs"
}
Run Code Online (Sandbox Code Playgroud)

在我的 Typescript 应用程序中,我安装了自己的包,并尝试导入它。然而,我观察到一件奇怪的事情:

// src/index.ts
import { Class } from 'my-awesome-lib'
import { Class } from '../node_modules/my-awesome-lib/dist/mjs/index.js';
Run Code Online (Sandbox Code Playgroud)

第一个导入语句指向而../node_modules/my-awesome-lib/dist/cjs/index.js不是mjs文件夹。第二个导入语句是我真正想要的。

谁能告诉我出了什么问题吗?是因为我以错误的方式发布了我的库吗?


附注

这是我的 tsconfig.json

{
    "compilerOptions": {
      "target": "es2020",
      "module": "es2020",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true,
      "declaration": true,
      "moduleResolution": "node",
      "outDir": "dist"
    },
    "include": ["src"]
}  
Run Code Online (Sandbox Code Playgroud)

ps 添加{"type": "module"}到应用程序的 package.json 也不会使此错误消失。

JΛY*_*ÐΞV 1

一切都按照设计的方式进行。你遇到的问题是你所期望的问题。您的“tsconfig.json”文件未正确配置 ESM 模块,但您希望它加载 ESM 包。

\n

看起来你正在使用它node16-strictest-esm-combined作为参考,如果你是这样,我可以告诉你这个配置大约一年前也让我失望了。

\n

我真的很喜欢 base/tsconfig 存储库,但仅适用于 CJS 配置。我不知道为什么他们使用 来配置名称中包含 ESM 的 tsconfig "moduleResolution": "node",但使用"node"作为值是告诉 TypeScript 发出解析为 CommonJS 模块的 JavaScript。

\n
这是官方文档的链接moduleResolution
\n

它表明下面的配置(这是我 100% 确定可以转换为 ESM 的配置)是要使用的正确配置。

\n

这直接来自我的一个项目,即“ESM”包。

\n
\n{\n    "compilerOptions": {\n      "lib": ["ESNext"], // Why not use NodeNext if it compiles down anyways\n      "target": "ES2021",\n\n      // Runtime/Module\n      "module": "Node16", // <- ESM Module Type\n      "moduleResolution": "Node16", // <- ESM Resolution\n      "esModuleInterop": false, // I added this to show what not to do\n\n      // Project Structure\n      "rootDir": "src",\n      "outDir": "build",\n      "declarationDir": "types",\n\n      "strict": true,\n      "alwaysStrict": false, // <- you can set this to false if your writing ESM\n      "skipLibCheck": true,\n      "forceConsistentCasingInFileNames": true\n    },\n\n    "include": ["src/**/*.ts", "src/**/*.mts"], // You might want to change this\n    "exclude": ["build", "types", "node_modules"]\n}  \n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

你有2个选择

\n

数字 1: \xc2\xa0 \xc2\xa0 要确保您的项目是 ES 模块:您需要重新编译您的项目。你有两个选择。您可以按照我喜欢的方式进行操作,如下所示:使用构建清理和强制标志。这些额外的标志确保删除所有不必要的内容,并且重新编译所有必要的内容,即使它没有被修改。这很重要,因为您正在改变模块类型。

\n

“ESM”中,您的.js文件应该使用关键字导入import,而不是“CJS” require()方法调用。\n因为tsc试图提高效率,所以它不会重新编译其中包含 require 的文件,因为相应的.ts文档没有被修改过。

\n
   $ tsc -b --clean\n   $ tsc -b --force\n
Run Code Online (Sandbox Code Playgroud)\n

数字 2: \xc2\xa0 \xc2\xa0 您可以采取的另一条路线是删除您的构建和类型目录,然后运行$ tsc​​. 这种方法也有效,但它需要额外的 shell 命令,或者单击鼠标,另一种方法是两个tsc命令。此外,第一个方法可以添加到 package.json 脚本属性中,稍后可以使用 快速再次调用npm run。您还可以为--build--force、 &--clean标志创建打字稿任务。

\n

\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0无论如何...

\n
\n

确保重新编译整个项目(包括所有文件)后,您的 javascript 文件使用import&export关键字,而不是requires("...")&module.exports属性 & 方法。只有这样您才会知道您确实拥有 ESM 模块。

\n

此时,您的软件包的 ESM 分支应该加载

\n

如果没有,那么您就知道您的软件包配置有问题,在这种情况下您可以给我发消息,我可以看看是否可以进一步提供帮助。

\n