Developing TypeScript package for Node.js, React and React Native (peerDependencies advice needed)

huangapple go评论72阅读模式
英文:

Developing TypeScript package for Node.js, React and React Native (peerDependencies advice needed)

问题

我正在开发一个 TypeScript 包,它应该像 "npm install " 一样简单地用于 Node.js、React 和 RN。

包的大部分功能在纯 JavaScript 中运行良好,但还有一些特殊文件,仅用于 React 或 RN。

例如,如果包安装在 React 项目中,用户可以从 /react 文件夹导入特殊的 hooks。

代码分离有两种方式:要么使用动态导入,要么强制用户自己导入包的一部分(比如 hooks,如果用户不导入它们,它们只是存在,不会被使用)。

动态导入的示例用法:

private async _setupRN(config: PackageConfig) {
    const { subscribeForAppStateChanges } = await import("./rn");
    ...
    );
  };

  if (config?.enableReactNative) {
    this._setupRN(config);
  }

所以问题是:
如果我只是将包复制到 ts-node / ts react / ts RN 项目中使用,那一切都正常工作。(即使使用特定于平台的功能)

如果我将包发布到 npm(.ts 文件)并安装它,那么它只在 React Native 中正常工作。例如,React 会在类型定义或导入/导出语句上引发错误。

如果我尝试在上传到 npm 之前运行 tsc --build package(.js 文件),然后安装它,那么它在 Node.js 和 React Native 中正常工作。但 React 项目会给我这个错误:

找不到模块: 错误: 无法解析 'react-native' 在 '.../node_modules/@appklaar/appklaar_sdk/rn'

现在,最有趣的部分:
即使我完全删除了使用 react-native 的文件的导入,也就是从 'react-native' 导入的代码应该永远不会被执行,我仍然会收到相同的错误。看起来 React 似乎会要求即使未使用的文件。

为什么会发生这种情况?如何修复它?react 和 react-native 是我的包的 peer 依赖项。我猜想可能是我构建 tsx --build 的方式不对(如果 RN 只是 peer 依赖项,它就不会构建,所以我也将它添加为 devDependency)。但如果这是问题,那么为什么在 Node.js 项目中一切正常呢?

我可以接受每次更新包时运行 tsc --build。我只是希望它在任何地方都能正常工作。

如果您想查看代码,这是包的链接:
https://www.npmjs.com/package/@appklaar/appklaar_sdk
RN 功能位于 "rn" 文件夹中。在 Connector 文件中使用了动态导入。
1.4.12 版本是以 .js 文件上传的,1.4.13 版本是以 .ts 文件上传的。

英文:

I am developing typescript package that should be used simple as "npm install <module>" for Node.js, React and RN.

Most of functionality of the package works in bare js without any problem, but also there is special files that should be used for react only or for RN only.

For example, if package is installed in React project, then user can import special hooks from <module>/react folder.

Code separating is done in two ways: either using dynamic imports, either forcing the user to import part of the package himself (like with hooks, they are just here, not used if not imported by user)

Dynamic import usage example:

  private async _setupRN(config: PackageConfig) {
    const { subscribeForAppStateChanges } = await import(&quot;./rn&quot;);
    ...
    );
  };

  if (config?.enableReactNative) {
    this._setupRN(config);
  }

So, the problem:
If I just use the package by copying folder to ts-node / ts react / ts RN projects then everything works fine. (Even when using platform-specific functionality)

If I publish package to npm as it is (.ts files) and install it, then it works fine only in React Native. React, for example, throws errors on type definitions or import / export statements.

If I go all-in, and try to tsc --build package before uploading to npm (.js files) and install it, then it works fine in Node.js and React Native. But React project gives me this error:

Module not found: Error: Can&#39;t resolve &#39;react-native&#39; in &#39;.../node_modules/@appklaar/appklaar_sdk/rn&#39;

And now, the most interesting:
Even If I completely remove import of files that uses react-native, so code that imports from 'react-native' should never be executed, I still get the same error. Looks like react is trying to require even unused files.

Why does it happen? How to fix it? react and react-native are my package's peer dependencies. I have a guess that probably I am making tsx --build wrong way (it wont build if RN is just peer dependency, so i added it as devDependency also). But if thats the problem, then why in Node.js project everything works fine?

I am ok with making tsc --build every time when I want to update the package. I just want this to work everywhere.

I do not want to split package into 3 packages for each platform. I am completely sure that I am close to solution.

Package link if you want to check the code:
https://www.npmjs.com/package/@appklaar/appklaar_sdk
RN functionality is inside "rn" folder. Dynamic import is used in Connector file.
Version 1.4.12 was uploaded as .js files, version 1.4.13 was uploaded as .ts files.

答案1

得分: 0

I ended up with "submodules" or "plugins" functionality.

That might be helpful:

  1. Dynamic imports is really good feature if you are using it in a regular project. But (for now) it's not stable enough for framework-specific development.

  2. Explanation for "plugin" idea that helped me to separate framework-specific code: For example, I had Network Interceptors functionality, and HTTPS requests listening is done differently for Node.js and React. Instead of trying to use dynamic imports depending on the framework, I just created a new folder "Interceptors" and put two different Interceptors files "InterceptorRN" and "InterceptorClassic" in it.

Then I added a new parameter for my package config, called "Interceptor" that the user should pass. The user imports a framework-suitable Interceptor from <lib/Interceptors> folder and passes it to the config. Then, the package will use the passed Interceptor.

Important: you should not import any file from the plugins folder yourself. Only if plugins are not imported can you be sure that in every framework they will be skipped when building / starting / compiling (ts).

英文:

I ended up with "submodules" or "plugins" functionality.

That might be helpful:

  1. Dynamic imports is really good feature if you are using it in regular project. But (for now) it's not stable enough for framework-specific development.

  2. Explanation for "plugin" idea that helped me to separate framework-specific code: For example, I had Network Interceptors functionality and https requests listening is done different ways for Node.js and React. Instead of trying to use dynamic imports depending on framework, I just created a new folder "Interceptors" and put two different Interceptors files "InterceptorRN" and "InterceptorClassic" in it.

Then I added new parameter for my package config, called "Interceptor" that user should pass. User imports framework suitable Interceptor from <lib/Interceptors> folder and passes it to config. Then, package will use passed Interceptor.

Important: you should not import any file from plugins folder yourself. Only if plugins are not imported you can be sure that in every framework they will be skipped when building / starting / compiling (ts).

huangapple
  • 本文由 发表于 2023年3月15日 20:57:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75745046.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定