#nodejs #package-json #utility #javascript #json-file #file-path #cli

app rn

基于package.json文件管理会话节点版本的命令行工具

2个版本

使用旧的Rust 2015

0.2.2 2017年7月23日
0.2.1 2017年7月22日

#19 in #package-json

MIT 许可证

800KB
736

包含 (Mach-o exe, 2.5MB) bin/macos_x64/rn

rn - Rust节点管理器

build status coverage report

护理您的节点环境,使其健康!

rn是另一个Node.js版本管理器,有点像rvm是Ruby版本管理器。它的主要目标是易于使用和快速执行,用于MacOS或Linux上的本地开发环境。

安装

RN包含一个bash脚本,用于在MacOS和Linux上安装自身,并在您的shell上执行简单的诊断。

curl -sSL https://gitlab.com/bff/rn/raw/master/install.sh | bash

检查它是否正常工作(任一平台)

rn

从源码编译

curl https://sh.rustup.rs -sSf | sh
git clone [email protected]:bff/rn.git
cd rn
cargo build --release

sudo cp target/release/rn /usr/local/bin/rn
cp bin/rn.sh .config/rn/
echo 'source $HOME/.config/rn/rn.sh' >> .bashrc

用法

在shell中设置节点版本

{
  "engines": {
    "node": "0.10.37"
  }
}
  1. 使用如上所示的精确三重组合在package.json中使用您的节点版本。
  2. 切换到包含package.json的目录
  3. 没有第3步。节点将作为您的shell中的该版本运行,直到切换到另一个节点项目。

列出版本

$ rn --list
0.10.37
4.3.0
6.4.9

配置

环境变量 RN_DIR

默认为 $HOME/.rn;这是rn将存储节点版本的地方,以及未来可能存储的元数据。节点二进制文件存储在$RN_DIR/versions

例如,如果您本地有node 5.7.1,并且未设置RN_DIR,您可以在/home/bryce/.rn/versions/v5.7.1找到本地副本。

功能比较

我认为了解rn的最好方法是将它与空间中的其他Node.js版本管理器进行比较。

项目 rn nvm nodenv[^1] n
设计目标 本地开发 本地开发 生产和开发 系统节点
功能集 小巧 大型 巨大 小巧
实现方式 Rust Bash/Zsh 所有shell Bash
完整Semver支持 从不 是的 是的 是的
每个壳版本 是的 是的 是的
全局版本 很快 是的 是的 是的
来源? 部分 是的 部分
解析package.json 是的
管理npm 很快
钩子cd 是的

设计决策

为什么选择Rust?

  • 我对Rust充满热情
  • 甚至比C或Go更甚,如果它能编译,那么几乎可以肯定它会JustWorks(tm)
  • 作为一个编译型语言,Rust在每次调用时都易于进入内存,这使得它在shell初始化方面比大型Bash脚本更快。
  • 虽然Bash非常适合小型脚本和单文件实用工具,但它不一定是一个理想的方式来构建一个大型项目,特别是当你想要维护它时。
  • Rust(和其他编译型语言)不太可能像shell脚本那样出现副作用问题。
  • Rust提供了对内存管理的极佳直觉。
  • Rust在包管理方面非常出色(与Go或Bash相比)。
  • Rust对调用C代码的FFI支持非常好(这使得它不太便携),我通过依赖关系充分利用了这一点。
  • Rust提供了探索并行化网络请求的机会。

与Rust一起工作的最大缺点是它不如Go或Bash便携。然而,我认为有足够多的技术理由可以证明使用Rust的合理性,尽管存在便携性问题。

为什么不实现完整的Semver?

我的主要目标是让开发者在他们的本地shell中的生活更好。如果package.json只指定了Node.js的主版本,那么就需要额外的网络和解析来查找可接受的版本以下载和安装。

此外,我在团队中工作过,发现小版本(未遵循Semver)会出现破坏性更改,或者即使是修补版本的变化也可能导致严重错误。我也相信,在开发环境和生产环境之间保持高度一致性是避免令人不快的惊喜的好方法。通过锁定确切的修补版本,我希望您能够避免一些严重的问题。

最后,如果这些(希望)听起来像是技术理由,没有说服您,我必须承认,我不想实现完整的Semver逻辑,我想要更快地完成这个项目。

出于所有这些原因,我没有在package.json解析中实现完整Semver的计划。

为什么有Node.js的每个shell版本?

同时运行多个Node.js应用程序通常很有用(用于微服务等)。通过分别配置每个shell的PATH,我可以确保每个终端会话都正好有它需要的node版本。把它想象成穷人的Docker。

为什么从大Bash文件中提取源码很糟糕?

在2015年MacBook Pro上,我看到提取数千行Bash脚本会增加打开新shell所需的初始化时间数秒。想象一下,每次你在Firefox中打开新标签页时,你都要等待2+秒才能让地址栏变得响应。你会立刻切换到Chrome。

从大Bash文件中提取源码是一种避免每次想要使用其功能时都重新加载它们的绝佳方法。然而,通过利用Rust(或任何编译型语言),它每次执行时都非常便宜地将机器代码加载到内存中。这也允许我跳过源一个大Bash文件所花费的昂贵的前置时间。

为什么覆盖cd

因为rvm这么做?!rvm实现的一个问题是,它有一个大型功能集,大部分是用Bash实现的,很容易出错。通过将cd包装在一个小、快、范围有限的Bash函数中,我可以轻松地让原生Rust代码做所有逻辑和繁重的下载node版本和更新PATH的工作,从而减少了出错的机会。然而,为了更新当前shell并且不让我对shell的更改在子shell中消失,Bash函数是一个明显的选择。说实话,我实在想不出其他更好的方法了。如果你知道一个,请打开一个问题并告诉我!

[^1]: 我个人没有使用过 nodenv,所以对我从文档中获取的 nodenv 描述不要完全相信。

工作原理

rn.sh 使用 Bash 函数替换了内置的 cd 工具。该函数检查是否存在 package.json 文件。如果找到 package.json 文件,则使用该文件的完整路径调用 rn

内部,rn 查找先前下载的 node 版本(如果有),并确定是否需要从 nodejs.org 下载所需版本的 Node.js 的 tarball。通过内存中的解压缩算法流式传输响应数据,并将解压缩的存档解压缩到 RN_DIR。

最后,无论是否发生下载,rn 都将尝试从路径中删除之前设置的任何 node 版本,并将新的 node 版本添加到路径的前面。它通过 STDOUT 打印出最终的期望 PATH 并退出 0。

rn.sh 接收 STDOUT 并替换当前 shell 中的 PATH。你现在可以运行你的项目了!

如果发生任何错误,rn 将将错误打印到 STDERR 并退出 1;路径将不会更新。已经实现了针对无效的 package.json 文件和不可用的 node 版本等场景的帮助性错误消息。此外,如有时间,还将实现上下文相关的错误消息。

依赖关系

~8–19MB
~296K SLoC