在 react-typescript 库中考虑多个对等依赖版本

问题描述

我正在将我的 React 组件库 react-esri-leaflet 转换为打字稿。该库要求用户安装 react-leaflet 作为 peerDependency,并提供对 react-leaflet version 3 (RLV3) 和 react-leaflet version 2 (RLV2) 的支持。对于认组件,我可以简单地做

import { createLayerComponent } from 'react-leaflet'

但是如果用户想要使用兼容 RLV2 的组件,他们可以单独导入这些组件(请随意查看库文档以了解如何)。大多数 RLV2 组件都以类似

import { withLeaflet,MapControl } from 'react-leaflet'

在转换为打字稿时,这会引发错误。在开发这个库时,我安装的 react-leaflet 安装了最新版本。 withLeafletMapControl 在该版本中不再存在。

为了解决这个问题,我遵循了 this answer 中的建议“Node 应用程序中相同的 npm 包的两个版本”。这允许我通过将 RLV2 别名为 'react-leaflet-v2' 来将 RLV2 视为源代码中的一个单独包,我可以这样做

import { withLeaflet,MapControl } from 'react-leaflet-v2'

太好了,现在打字稿很高兴,我可以将适当版本的 react-leaflet 导入适当的组件,并且打字正确。但是,当我使用 tsc 编译回 js 时,我的 V2 组件中使用的包名称仍然是 'react-leaflet-v2'

问题在于库的最终用户将使用 RLV2 或 RLV3,并且在他们的项目中将简单地称为 'react-leaflet'。库的编译文件需要能够找到 'react-leaflet',而不是某些别名,无论它们使用的是什么版本。

如何在开发中为两个依赖版本保持类型安全,但让我的用户不用担心,这意味着只需安装他们想要使用的 react-leaflet 版本,从库中导入正确的组件,以及组件是否在其项目中找到了 'react-leaflet' 依赖项?编译时是否可以告诉 tsc'react-leaflet-v2' 重命名'react-leaflet'?还有其他解决方案吗?

解决方法

所以我想出了一个办法。它并不完美,但基本上是:

使用 Babel

我没有使用 tsc 转译我的 ts 文件,而是使用了 babel,仅用于那些具有别名节点模块导入的文件。我找到了 babel-plugin-transform-rename-import,它可以在编译时重命名导入。将它添加到标准 babel 配置中,我有这个:

{
  "presets": ["@babel/preset-env","@babel/preset-typescript"],"plugins": [
    "@babel/plugin-proposal-object-rest-spread","transform-react-jsx","@babel/plugin-proposal-class-properties",[
      "transform-rename-import",// <--- solution here
      { "original": "react-leaflet-v2","replacement": "react-leaflet" }
    ]
  ]
}

这是 react 打字稿文件的标准设置,带有一些额外的 bella 和 whistle。如您所见,它会将所有导入从“react-leaflet-v2”重命名为“react-leaflet”。

考虑到我的项目被组织为将所有这些 RLV2 文件都放在它们自己的文件夹中,我编写了一个脚本来使用常规的 tsc 命令来编译所有常规组件,但是使用带有该配置的 babel 只转换v2 目录中的文件。然后我使用 tsc --emitDeclarationOnly true 为 v2 文件编写 .d.ts 文件。

注意这很有效,因为我的库没有从“react-leaflet-v2”导入任何纯打字稿声明,这意味着没有 import { TypeOrInterfaceOrEnum } from 'react-leaflet-v2' - 所有导入都只是组件或函数。如果我从 react-leaflet-v2 导入类型信息,我的 import something from 'react-leaflet-v2' 文件中仍然会有 .d.ts,我也需要以某种方式转换它们。也许 babel 也可以做到这一点,但我现在没有必要去探索它。