nvm 鸿蒙 PC 适配全记录:从 Shell Function 到 HNP 原生交付
欢迎加入开源鸿蒙 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/ 目录:
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/ # 功能矩阵与验收报告
终端中执行一次版本切换时,实际路径如下:
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 版本:
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:
cd ohos
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
assembleHap --no-daemon
签名产物通常位于:
ohos/entry/build/default/outputs/default/entry-default-signed.hap
连接 HarmonyOS PC 真机后安装并启动:
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:
. /data/service/hnp/bin/nvm.sh --no-use
nvm --version
nvm use default
node --version
npm --version
如果 HiShell 已暴露可执行的 HNP link,也可以使用:
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 上的多版本管理能力才能从“内置版本可用”自然扩展到完整的在线版本生态。
更多推荐




所有评论(0)