欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/
欢迎在 PC 社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_nvm
一、为什么要适配 nvm
Node.js 项目对运行时版本非常敏感。同一台开发设备上,旧项目可能仍依赖 Node.js 18,新项目已经切换到新的 LTS,构建脚本、前端工具链和全局 CLI 又可能各自限定不同版本。nvm 之所以成为 Node.js 开发环境中的基础工具,正是因为它把版本安装、切换、别名、.nvmrc 项目约束和指定版本执行统一到了终端会话中。
鸿蒙 PC 正在逐步补齐桌面开发场景。只有编辑器和图形界面还不够,真实项目最终仍要落到运行时、包管理器和构建命令上。将 nvm 适配到鸿蒙 PC,可以让 Node.js 开发者继续沿用熟悉的 nvm use、nvm current、nvm run 和 .nvmrc 工作方式,也能验证 HNP 在开发工具分发、公共命令链接、原生运行时携带和终端初始化方面的完整链路。
本次适配基于 nvm 0.40.6。项目没有另写一套只用于演示的图形化版本管理器,而是保留上游 nvm.sh、nvm-exec、install.sh 和补全脚本,将它们与 OpenHarmony arm64 Node.js 运行时一起打入 HNP。配套 HAP 负责安装、品牌展示和使用指引,版本切换与 Node.js 开发工作仍在鸿蒙 PC 自带的 HiShell 中完成。
当前真机安装的 HNP 版本为 0.40.6.ohos8,内置 Node.js v24.2.0、npm/npx 11.3.0,BundleName 为 org.nvm.ohos。验收设备为 HarmonyOS PC 2in1、arm64-v8a、API 23。
二、先明确适配边界:nvm 不是一个普通桌面应用
nvm 的核心不是可独立运行的二进制程序,而是加载到当前终端进程中的 Shell Function。nvm use 需要直接修改调用者的 PATH、NVM_BIN 和当前 Node.js 版本;如果把它简单包装成一个子进程,子进程退出后这些环境变化也会一起消失。因此,鸿蒙侧不能只提供一个名为 nvm 的可执行文件,还必须提供能够被当前 HiShell 会话加载的初始化入口。
另一方面,传统 nvm 默认从 Node.js 官方或镜像站下载 Linux、macOS 等平台产物。HarmonyOS PC 对 HNP 公共挂载、可执行文件签名、应用沙箱和目标 ABI 有自己的要求,普通 Linux arm64 归档不能因为架构同为 AArch64 就被视为鸿蒙原生运行时。当前方案据此拆成四层:
| 层次 | 解决的问题 | 当前实现 |
|---|---|---|
| 上游功能层 | 保留真实 nvm 命令与版本解析逻辑 | 原样携带 nvm 0.40.6 的核心 Shell 文件 |
| HNP 交付层 | 将命令、脚本和 Node.js 运行时随应用安装 | 使用 hnpcli 生成 nvm.hnp,配置公共命令链接 |
| 终端初始化层 | 让 nvm use 修改当前 HiShell 会话 |
nvm-init 输出初始化脚本,nvm.sh 复制到可写的 $HOME/.nvm 后被 source |
| 运行时层 | 提供真机可执行的 Node.js、npm 和 npx | HNP 内置 OpenHarmony arm64 Node.js v24.2.0,并生成用户目录 wrapper |
配套 ArkUI 页面只展示版本、初始化方法和已验证命令,不在页面中伪造版本列表或命令结果。真实能力以 HiShell 中执行上游 nvm Shell Function 的结果为准。
三、鸿蒙版本的工程结构
仓库根目录继续保留 nvm 上游源码,HarmonyOS 适配集中在 nvm/ 与 ohos/ 目录:
text
ohos_nvm/
├── nvm.sh # 上游 nvm 核心 Shell Function
├── nvm-exec # 上游指定版本执行入口
├── install.sh # 上游安装脚本
├── bash_completion # 上游 Bash 补全
├── build_hnp.sh # HNP staging、launcher 编译、校验与打包
├── nvm/
│ ├── hnp.json # HNP 包名、版本和公共链接定义
│ ├── bin/
│ │ ├── nvm.sh # HarmonyOS 可 source 的 bootstrap
│ │ ├── nvm-init # 输出当前 Shell 初始化语句
│ │ ├── nvm-command.sh # 一次性 nvm 命令入口
│ │ ├── curl # HarmonyOS 下载兼容封装
│ │ └── tar # HarmonyOS 解包兼容封装
│ └── src/ohos-sh-launcher.c # 由 OHOS clang 编译的原生启动器
└── ohos/
├── AppScope/ # 应用名称、图标与版本配置
├── entry/src/main/ets/pages/
│ └── Index.ets # nvm 使用入口与能力说明页面
├── entry/src/main/hnp/arm64-v8a/ # HAP 内的 nvm.hnp 注入位置
├── handoff/ # 构建、签名和真机验收脚本
├── evidence/ # 真机验证材料
└── reports/ # 功能矩阵与验收报告
终端中执行一次版本切换时,实际路径如下:
text
HiShell 当前会话
└── source /data/service/hnp/bin/nvm.sh
├── 解析 HNP 公共链接的真实安装目录
├── 将上游脚本复制到 $HOME/.nvm
├── 建立内置 Node.js 的用户可写 wrapper
└── source $HOME/.nvm/nvm.sh
└── nvm use default
└── 更新当前会话 PATH
└── Node.js v24.2.0 / npm 11.3.0
HNP 同时提供 nvm-init、nvm-profile、nvm-exec、nvm-ohos-node、nvm-ohos-npm、nvm-ohos-npx 等链接。通用的 node、npm 和 npx 名称在 nvm use 后由 $HOME/.nvm/versions/node/.../bin 暴露,既保持 nvm 的版本切换语义,也避开直接从公共 HNP 挂载位置执行 Shell 脚本带来的限制。
四、真机上的五个核心功能验证
以下五张截图均在已连接的 HarmonyOS PC 真机上重新启动应用并实际操作后采集,分辨率为 3120×2080。第 1 张来自已安装 HAP,第 2 至第 5 张来自设备自带 HiShell;命令通过真机界面输入并等待设备端返回结果,不是开发机终端输出的拼接图。
1. HAP 页面正常启动并识别 HNP 版本
应用启动后显示 nvm 官方标识、上游版本 0.40.6、HNP 版本 0.40.6.ohos8,下方给出终端初始化、版本查询和切换命令。

这一屏验证了签名 HAP 的安装、Stage 模型 Ability 启动、2in1 桌面窗口渲染以及 HNP 随包交付。页面明确把 HiShell 作为实际操作入口,避免用户误以为点击页面中的文本就等同于完成版本切换。
2. 初始化真实 nvm,并运行内置 Node.js、npm 和 npx
在 HiShell 中 source HNP 公共链接下的 nvm.sh,随后执行 nvm --version、nvm use default、node --version、npm --version 和 npx --version。

终端返回 nvm 0.40.6,并提示 Now using node v24.2.0 (npm v11.3.0);Node.js、npm 和 npx 随后分别返回 v24.2.0、11.3.0 和 11.3.0。这组结果同时覆盖了 HNP 链接、Shell Function 加载、默认别名、PATH 切换和 OpenHarmony 原生运行时执行。
3. 使用 .nvmrc 约束项目版本
测试在用户可写目录创建 nvm-doc 项目,将 v24.2.0 写入 .nvmrc,然后不带版本参数执行 nvm use,并通过 nvm current、nvm which current 检查选择结果。

nvm 找到了 /storage/Users/currentUser/nvm-doc/.nvmrc,切换到 v24.2.0,最终返回用户目录下的 Node.js wrapper 路径。这个结果说明项目级版本约束和当前终端状态是连通的,而不是只在 HNP 包内保存了一份固定版本信息。
4. 完成 npm 项目初始化、配置读写和 JavaScript 执行
在同一项目目录执行 npm init -y 创建 package.json,再用 npm pkg set 修改版本号,通过 npm pkg get 读取项目名称和版本,最后执行 node -p process.version。

真机成功写入 /storage/Users/currentUser/nvm-doc/package.json,项目名为 nvm-doc,修改后的版本为 1.0.1,Node.js 返回 v24.2.0。这一屏验证的不是单独的版本打印,而是 Node.js 与 npm 在真实用户目录中的文件读写和项目操作闭环。
5. 从网络获取远程 LTS 版本索引
最后执行 nvm ls-remote --lts | tail -n 12。nvm 通过设备网络读取远程版本索引,并输出 Krypton LTS 的末尾十二条记录。

本次返回结果从 v24.11.1 延续到 v24.19.0,最后一行标记为 Latest LTS: Krypton。这说明 HNP 内的下载兼容层、证书链、网络权限和上游版本解析逻辑能够协同工作。需要注意的是,远程"发现版本"与"安装后可在 HarmonyOS 执行"是两个不同的验收项;后者仍取决于远端是否提供经过验证的 HarmonyOS 原生 Node.js 产物。
五、适配过程中最棘手的几个问题
难点一:nvm use 必须影响当前 Shell,普通子进程做不到
很多命令行工具可以通过一个 ELF 启动器直接运行,但 nvm 的核心价值恰恰是修改当前会话。若用户运行的是一个独立的 nvm 进程,即使该进程内部成功修改了 PATH,退出后父级 HiShell 也不会收到变化。
项目因此同时保留两类入口:nvm-command.sh 适合版本查询等一次性操作,nvm-init 和 sourceable nvm.sh 则用于真正的会话初始化。用户执行 eval "$(nvm-init)" 或直接 source HNP 中的脚本后,上游 nvm Function 才会进入当前 HiShell,nvm use、nvm deactivate 和别名操作也才能保持原有语义。
难点二:HNP 公共目录、可写目录和可执行策略必须分开处理
HNP 安装后位于公共挂载目录,适合分发稳定文件,但 nvm 运行时会更新 alias、缓存、已安装版本和脚本状态,不能把整个工作目录都放在只读或受限位置。与此同时,HarmonyOS 对可执行文件签名和公共包内脚本执行有明确要求,不能照搬桌面 Linux 的"解压后 chmod 即运行"。
当前 bootstrap 会先解析公共链接的真实路径,再把上游 nvm.sh、nvm-exec、install.sh 等复制到 $HOME/.nvm。内置 Node.js 仍由签名 HNP 提供,用户目录只创建轻量 wrapper。nvm-init、npm、npx 等直接入口则使用 OpenHarmony SDK clang 编译的原生 launcher,由 launcher 调用 /bin/sh 和对应脚本。这样把不可变交付、用户状态与命令入口分成了清晰的三部分。
难点三:AArch64 相同,不代表 Linux Node.js 能在 HarmonyOS 直接运行
上游 nvm 的平台解析面向 Linux、macOS、FreeBSD 等传统目标。早期若把 HarmonyOS 仅映射为 Linux arm64,下载到的可能是 linux-arm64-musl 归档;它在文件名和 ELF 架构上看似接近,却不等于经过 HarmonyOS SDK 构建、签名并在真机验收的运行时。
当前交付包因此固定内置 node-v24.2.0-openharmony-arm64,先保证一条可复现的本地开发主链路。远程版本列表仍沿用上游解析能力,但任意远程安装不会因为下载动作开始就被计为完成。只有远端提供兼容产物,并在真机通过 Node.js、npm、项目文件读写和版本切换验证,才能进入可用范围。
难点四:npm 在用户 HOME 与项目目录之间存在配置歧义
nvm 初始化后需要从用户 HOME、普通项目目录和 HNP 内置运行时之间切换。npm 会同时处理用户级 .npmrc 和项目级配置;当当前目录就是 HOME 时,某些路径会把同一个文件重复解释为用户配置和项目配置,引发前缀警告或覆盖关系异常。
适配层为 npm 设置隔离的 NPM_CONFIG_USERCONFIG,并在运行时对 HOME 项目路径做兼容处理。Node.js、npm、npx 的用户目录 wrapper 也显式传递这一策略。真机截图中的 npm init -y、npm pkg set/get 和 HOME 下版本查询,都是这部分处理后的结果。
难点五:开发机 hdc shell 不能替代用户真正使用的 HiShell
hdc shell 适合安装检查、包信息查询和自动化烟测,但它与桌面用户打开的 HiShell 并不共享完全相同的 PATH、HNP 链接和会话初始化过程。只在 hdc shell 中调用物理路径,即使返回成功,也不足以证明普通用户能找到并使用 nvm。
本次验收因此把 HAP 安装与启动交给 HDC,把 nvm、Node.js 和 npm 的使用验证放到真机 HiShell 窗口中完成。五张截图中涉及命令结果的四张都保留了 HiShell 界面和输入命令,确保文档描述对应真实用户路径。
六、构建、安装与运行
本项目不是 Electron 或 Qt 工程,HAP 页面使用 ArkTS/ArkUI,命令行能力通过 HNP、POSIX Shell 和 OpenHarmony 原生 launcher 交付。开发机需要安装 DevEco Studio、HarmonyOS/OpenHarmony SDK,并确保 SDK 中存在 hnpcli、OHOS clang、Hvigor 和 HDC。
首先在仓库根目录构建 HNP。下面显式指定与本文真机一致的 HNP 版本:
bash
cd ohos_nvm
HNP_VERSION=0.40.6.ohos8 ./build_hnp.sh
构建脚本会完成以下工作:
- 整理上游 nvm 文件和 HarmonyOS bootstrap;
- 下载或读取 OpenHarmony arm64 Node.js v24.2.0,并校验 SHA-256;
- 使用
aarch64-unknown-linux-ohos-clang编译命令 launcher; - 检查脚本语法、执行本地 bootstrap smoke test;
- 使用
hnpcli生成ohos/out/nvm.hnp; - 将 HNP 复制到 HAP 的
entry/src/main/hnp/arm64-v8a/。
随后构建签名 HAP:
bash
cd ohos
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
assembleHap --no-daemon
签名产物通常位于:
text
ohos/entry/build/default/outputs/default/entry-default-signed.hap
连接 HarmonyOS PC 真机后安装并启动:
bash
hdc list targets
hdc install -r ohos/entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -b org.nvm.ohos -m entry -a EntryAbility
打开 HiShell 后初始化 nvm:
bash
. /data/service/hnp/bin/nvm.sh --no-use
nvm --version
nvm use default
node --version
npm --version
如果 HiShell 已暴露可执行的 HNP link,也可以使用:
bash
eval "$(nvm-init)"
如终端 PATH 尚未暴露公共 link,可从 HAP 页面显示的 HNP 物理路径调用 nvm-init.sh。HNP 版本变化时,物理目录名也会变化,因此面向日常使用更推荐 /data/service/hnp/bin/nvm.sh 或 nvm-init 链接。
七、当前已覆盖的能力与明确边界
当前版本已经在 HarmonyOS PC 真机上跑通:
- 签名 HAP 安装、Ability 启动、HNP 随包提取与公共链接;
- 上游 nvm 0.40.6 Shell Function 初始化;
nvm --version、nvm ls、nvm current、nvm which;nvm use default、默认 alias 和当前终端 PATH 切换;- 内置 OpenHarmony Node.js v24.2.0 的 JavaScript 执行;
- npm/npx 11.3.0 版本查询和用户目录执行;
.nvmrc项目版本发现与切换;npm init、npm pkg set/get和package.json文件读写;nvm run、nvm exec与缓存目录管理;nvm ls-remote --lts远程版本索引获取;- HNP staging、launcher 交叉编译、脚本校验和打包流程。
当前没有作为完整适配能力承诺的部分包括:
- 任意远程 Node.js 版本的下载、安装与真机可执行闭环;
- 在设备侧从 Node.js 源码完成完整编译安装;
- io.js 历史版本的 HarmonyOS 原生运行时;
- HiShell 中的 Bash completion;
- 任意包含 Native Addon 的 npm 依赖编译;
- 与桌面 Bash、zsh 完全一致的 profile 自动加载行为;
- 把 Linux arm64 或 Linux musl 产物直接视为 HarmonyOS 兼容产物。
因此,当前成果更准确的定位是"以 HNP 交付真实 nvm 0.40.6,并围绕内置 OpenHarmony Node.js v24.2.0 跑通日常项目开发主流程"。远程版本发现已经可用,但完整的多版本在线安装还需要稳定的 HarmonyOS 原生 Node.js 产物仓库配合。
八、总结
nvm 的鸿蒙 PC 适配并不是给 Shell 脚本套一个窗口。真正需要保留的是它与当前终端的关系:版本切换必须影响调用者 PATH,.nvmrc 必须落到真实项目目录,Node.js 和 npm 必须能够读写用户文件,远程列表与本地运行时还要保持清晰的可信边界。
本项目用 HNP 解决脚本、launcher 和运行时的统一交付,用 sourceable bootstrap 把上游 nvm 带回当前 HiShell,再通过用户可写 wrapper 连接签名 HNP 中的 OpenHarmony Node.js。HAP 页面、终端初始化、默认版本切换、.nvmrc、npm 项目操作和远程 LTS 查询五组真机结果,构成了一条从安装到实际开发使用的完整证据链。
后续工作的关键不是简单增加更多版本号,而是建立可持续的 HarmonyOS Node.js 构建与发布渠道,并对每个运行时做 ABI、签名、npm、文件系统和项目脚本验收。只有远程版本产物也遵循同一套质量门槛,nvm 在鸿蒙 PC 上的多版本管理能力才能从"内置版本可用"自然扩展到完整的在线版本生态。