image.png

封面图是 mintlify,一个用 AI 来帮你写注释的神器,写的还挺好的。

众所周知,现在前端都喜欢写 rust, 要是不会用 go 或者 rust 双修都不好意思出去见人。作有追求的程序员我们总不能写个hello word 就满足了,我们至少要满足工程化,所以我用 rust 写了一个生成 changelog 的工具,顺便分享一些 node 和 rust 的工程化的问题。

📖 名词解释

👓 交叉编译

rust 在每个平台都有相关都会生成相应的可执行文件。rust 支持非常多的平台和cpu,为了方便开发者, rust 支持了很棒的交叉编译,可以在相应的平台的编译出相应的二进制文件。

  1. rustc --print target-list | pr -tw100 --columns 3
  2. ----------------------->
  3. aarch64- riscv64gc-unknown-freebsdnux-musl
  4. aarch64-apple-io i386-ap riscv64gc-unknown-linux-gnu
  5. aarch64- i586-pc riscv64gc-unknown-linux-musl
  6. aarch64-apple-io riscv64gc-unknown-none-elf
  7. aarch64- riscv64imac-unknown-none-elfsl
  8. aarch64-fuchsia s390x-unknown-linux-gnun
  9. aarch64-kmc-soli s390x-unknown-linux-musl
  10. aarch64-linux-an sparc-unknown-linux-gnu
  11. aarch64-pc-windo sparc64-unknown-linux-gnu
  12. aarch64-unknown- sparc64-unknown-netbsd
  13. aarch64-unknown- sparc64-unknown-openbsd
  14. aarch64- i686-un sparcv9-sun-solaris
  15. aarch64- thumbv4t-none-eabimusl
  16. aarch64- i686-un thumbv6m-none-eabi
  17. aarch64-unknown- thumbv7a-pc-windows-msvc
  18. aarch64-unknown- thumbv7a-uwp-windows-msvc
  19. aarch64- i686-uw thumbv7em-none-eabi
  20. aarch64-unknown- thumbv7em-none-eabihf
  21. aarch64-unknown- thumbv7m-none-eabi
  22. aarch64-unknown- thumbv7neon-linux-androideabi
  23. aarch64- mips-un thumbv7neon-unknown-linux-gnueab
  24. aarch64- thumbv7neon-unknown-linux-muslea
  25. aarch64_ thumbv8m.base-none-eabic
  26. aarch64_ thumbv8m.main-none-eabimips64-unknown-linux-gnuabi64
  27. arm-linu thumbv8m.main-none-eabihfux-muslabi64
  28. wasm32-unknown-emscripteninux-gnuabi64
  29. wasm32-unknown-unknownwn-linux-muslabi64
  30. arm-unkn mipsel- wasm32-wasi
  31. arm-unkn wasm64-unknown-unknownu
  32. armebv7r x86_64-apple-darwinwn-linux-musl
  33. armebv7r x86_64-apple-iosknown-linux-uclibc
  34. armv4t-u mipsel- x86_64-apple-ios-macabi
  35. armv5te- x86_64-apple-tvosn-linux-gnu
  36. x86_64-fortanix-unknown-sgxn-linux-gnu
  37. mipsisa64r6-unknown-linux-gnuabi x86_64-fuchsia
  38. armv6-unknown-fr mipsisa64r6el-unknown-linux-gnua x86_64-linux-android
  39. armv6-un msp430- x86_64-pc-solaris
  40. armv6k-nintendo- x86_64-pc-windows-gnu
  41. armv7-ap x86_64-pc-windows-msvc-unknown-freebsd
  42. armv7-li x86_64-sun-solarisnown-linux-gnu
  43. armv7-un x86_64-unknown-dragonflyinux-gnuspe
  44. armv7-un x86_64-unknown-freebsdusl
  45. armv7-un powerpc x86_64-unknown-haiku
  46. armv7-un x86_64-unknown-hermitd
  47. armv7-un powerpc x86_64-unknown-illumos
  48. powerpc x86_64-unknown-l4re-uclibc
  49. armv7-un x86_64-unknown-linux-gnu
  50. armv7-wr x86_64-unknown-linux-gnux32
  51. armv7a-k x86_64-unknown-linux-muslsl
  52. armv7a-k powerpc x86_64-unknown-netbsd
  53. armv7a-n x86_64-unknown-noneunknown-freebsd
  54. armv7a-n x86_64-unknown-none-hermitkernel-gnu
  55. armv7r-n x86_64-unknown-none-linuxkernelx-musl
  56. armv7r-n x86_64-unknown-openbsdwn-linux-gnu
  57. armv7s-a x86_64-unknown-redoxnown-linux-musl
  58. asmjs-un x86_64-unknown-uefie-elf
  59. avr-unkn x86_64-uwp-windows-gnue-elf
  60. bpfeb-unknown-no x86_64-uwp-windows-msvc
  61. bpfel-un x86_64-wrs-vxworksunknown-none-elf

👓 n-api

https://nodejs.org/api/n-api.html

N-API 是一个规范,主要是为了解决 Node.js 和 C++ 模块之间的通信。

  1. #define NAPI_VERSION 3
  2. #include <node_api.h>

我们通过 node 调用 rust 也是借用了这个方式。

👓 cargo 和 rustup

rust 的包管理工具,比 npm 好多了。
rustup 是 rust 的管理工具,一般用来安装 交叉编译的工具和 rust 本身。

image.png

👓 optionalDependencies

optionalDependencies 代表可选的依赖,即使这个依赖安装失败了也可以继续使用,配合 package.json 中的 os 与 cpu 字段可以让不需要包安装失败,用来配置分发我们底层包。

  1. "optionalDependencies": {
  2. "@umijs/doctor-darwin-arm64": "^0.0.11",
  3. "@umijs/doctor-darwin-x64": "^0.0.11",
  4. "@umijs/doctor-linux-x64-gnu": "^0.0.11",
  5. "@umijs/doctor-win32-x64-msvc": "^0.0.11"
  6. },

👓 artifacts

构件 或者说叫产物,一般用于 CI/CD 中生成的某些需要持久化的东西,可以在多个 CI 进程中公用。与 cdn 不同的是,会在一次 CI/CD 中走完所有的生命周期。

🚀 开始开发

⁉️ 为什么要用 rust 开发?

rust 拥有很多底层的生态的,比如更加好用并且性能更的 git 操作库,git 官方支持拥有非常强的性能,可以在 2s 内处理完 procomponents 的所有 commit,并且生成文件。

  1. use git2::Repository;
  2. let repo = match Repository::init("/path/to/a/repo") {
  3. Ok(repo) => repo,
  4. Err(e) => panic!("failed to init: {}", e),
  5. };

其次还有更快的网络请求库,比 node 要快 25 倍,虽然看起来不起眼,但是生成changelog 很重要的一步就是从 github 中读取 commit 的 pr 和创建人,根据 commit 的数量来看,这个请求将会非常密集,并且需要序列化多次 JSON,社区是由文章进行过比较的, rust 和 go 的性价比会好很多, tnpm 的raid 模式也通过将网络请求层换成 rust 获得了极大的性能提升。

Rust 拥有最好的并发生态系统,其次是 Java 和 Golang,它们已经有了成熟的选择。Node.js和Deno虽然不如其他节点好,但也提供了一个底层的生态系统。

对于这种需要高性能的密集计算任务 rust 还是比较合适的,而且输入和输出都会很简单。

当然我觉得我空有 rust 的才能,没办法落地也很难受。

🆕 创建项目

首先最重要的就是创建文件夹了。 我们这里使用 NAPI-RS 的架构,它可以帮助我们快速生成一个的脚手架, 这里可以看到所有的文档。

https://napi.rs/docs/introduction/getting-started

  1. // 安装依赖
  2. yarn global add @napi-rs/cli
  3. # or
  4. npm install -g @napi-rs/cli
  5. # or
  6. pnpm add -g @napi-rs/cli
  7. // 新建项目
  8. napi new

新建的时候会让我们选择平台,这里我们推荐增加一个 aarch64-apple-darwin 就好了,android 和 别的 linux 平台兼容性不是很好。

image.png

选择完成之后就是这样的场景:
image.png

这里只带了几个script ,我们可以用这个东西来编译和发布。
image.png

build 之后会生成 index.js 和 **.node 文件,这两个文件就是我们最后的产物。

✍ 开发功能

开发没什么好说的,rust 写起来那是相当简单随意。

image.png

这里有我用了两个比较复杂的库,一个用来解析 git 仓库,一个用 swc 来解析 js 的 AST。

  1. git2 = "0.14"
  2. deno_ast = { version = "0.5.0", features = ["transforms", "utils", "visit", "view"] }

rust 的文档是真的不错。只有在lib 中加了 [napi] 的方法才会被暴露到 js 中,效果看起来是这样的。

image.png

方法中最好只用 String 来当参数,复杂的参数性能多的,转化成的开销不算小的,这是一个简单的 a+b 的代码,因为桥接的开销反而比 node.js 运行还要慢。

image.png

⚠️ 测试

写好代码自然要测试一下了,在 rust 中也提供了相应的方案。

image.png

断言写起来和我们的 node 也差不多

  1. assert_eq!(2 + 2, 4);

写起来大概是这样的,我们可以调用 cargo test 或者 vscode 中点击小图标运行。
image.png

👩🏻‍💻 发布

对于我们这个框架的发布来说最难得就是发布了,因为我需要编译多个操作系统,并且同时发布他们。

image.png

基本思路是用不同得操作系统打包出相应操作系统得二进制文件,然后上传到 artifact

最后发布得时候我们将 artifact 移动到相应得 npm 包中,并且发布到 npm 上。

image.png

发布之后得效果:

image.png

我们通过 npm 依赖来获取安装到得包。

image.png

在 index 中我们通过不同得平台和不同得 cpu 来加载不同得node 文件,达到跨平台得效果。

image.png

其实这一步最难得就是编译了,rust 是出了名得编译速度慢,依赖操作系统又让他编译非常辛苦,经常出一些 java 和 node 不会碰到的问题。

这是我得 arm 版本 mac 得编译脚本,看这些脚本就知道我踩了多少坑。

image.png

而这个是 x86 的编译。

image.png

这也是我推荐只支持主流操作系统的原因。至于为什么支持的 arm 的 mac,主要原因还是我有。

😺 使用

度过了艰难的发布和部署,使用就是相当的简单和随意了,因为参数和返回相对比较简单,所以我们还会的到很不错的类型定义,调用起来可以说是相当舒服了。

image.png

ts 定义也很完整。

image.png

我这个里面一共带了四个方法,分别是:

  • 生成上次tag 的 changelog
  • 生成所有的changelog
  • 检查路由配置的对不对
  • 检查 npm 发包是不是真的成功了

检查路由配置是为了以后些基于 rust 的lint 规则,npm实在是太卡太慢,太复杂了。

🗨️ 简单的说两句

最后经过这次的处理,我感觉这个东西最方便的还是写贴近底层的代码,虽然成本不算太大,但是收益也不大,尤其是你还要编译到各个操作系统,这个如果没有好的 CI 做起来会非常辛苦。

这里只推荐这几种应用:

  • 调用 图片处理,视频处理,大文件读取等 io,cpu,gpu 密集的功能
  • cli 功能的开发,新建脚手架之类的会舒服很多,因为有很多方便的库
  • 短时间内执行密集操作的。 比如说下载几百个小文件
  • ❤️ 为爱发电