Use Electron as the Desktop Node runtime
使用 Electron 作为 Desktop 的 Node 运行时
Shipping an upstream Node executable alongside Electron duplicates the JavaScript runtime. Desktop needs one runtime for its Host and package scripts without requiring users to install Node.
English
Problem
Shipping an upstream Node executable alongside Electron duplicates the JavaScript runtime. Desktop needs one runtime for its Host and package scripts without requiring users to install Node.
Decision
Desktop runs the shared Web Host and bundled pnpm through its own Electron executable with ELECTRON_RUN_AS_NODE=1. The primary runtime separately supplies Node for Office authoring. The target Electron distribution supplies both the packaging input and the runtime used to prepare and verify production dependencies; release metadata records its actual Node version. Development uses the installed Electron distribution. The installed CLI reuses this execution environment.
This supersedes the separate-Node choice in the packaging decision and bundled-runtime decision. Their independent plugin storage remains applicable. The Web wrapper retains the shared profile runner and HTTP transport.
Consequences
Host and pnpm launches pass --expose-internals: the bundled Cordis loader uses Node's internal ESM loader, while its native builtin accessor cannot locate the required symbol in Electron 44. The explicit flag makes that loader available without modifying Cordis. The RunAsNode fuse remains enabled.
The Host inherits Desktop’s PATH, merged with the login-shell environment on macOS and Linux, without Desktop’s private bin directory, so PTC and agent shells cannot resolve internal launchers through that directory. DSH_DESKTOP_NODE_EXECUTABLE is injected only for package installation. Package-script environments prepend a small node shell launcher that forwards arguments to the current Electron executable. This supports shell lifecycle scripts without a system Node installation. On Windows it is node.cmd, not a replacement node.exe; third-party code that directly spawns the literal node executable without a shell must use process.execPath or provide its own runtime. Child processes inherit RunAsNode; worker threads inherit the Host's arguments. Desktop does not emulate upstream OpenSSL behavior or rebuild arbitrary third-party native addons automatically.
Electron's Node patches and native ABI are release compatibility obligations. The packaged native smoke exercises pnpm shell scripts without system Node on PATH, terminal output through the Windows shell, Koffi, Sharp, and HTML conversion. The Host smoke loads an external plugin sharing Cordis and serves its route through the real Web application. Platform signing and installed-application qualification remain required; Windows results do not establish macOS compatibility. Windows token signing runs serially and retains the first failure, preventing queued tasks from repeating a rejected PIN.
Alternatives considered
A separate Node executable decouples the Host from Electron's runtime but adds another binary, download, signature, and version selection for that role. Electron RunAsNode reuses the GUI executable. Production packages reside in ASAR with native resources unpacked; packaging and physical-resource resolution remain separate from the execution-engine choice.
中文
问题
在 Electron 之外携带上游 Node 可执行文件会重复分发 JavaScript 运行时。Desktop 需要让 Host 和包脚本共用一个运行时,无需用户安装 Node。
决策
Desktop 通过自己的 Electron 可执行文件运行共享 Web Host 和内置 pnpm,并设置 ELECTRON_RUN_AS_NODE=1。主运行时另行提供 Office 创作使用的 Node。目标 Electron 分发包同时作为打包输入,以及准备和验证生产依赖的运行时;发布元数据记录其实际 Node 版本。开发模式使用已安装的 Electron 分发包。安装包中的 CLI复用此执行环境。
此决策替代打包决策和内置运行时决策中的独立 Node 选择。独立插件存储仍然适用。Web 薄壳保留共享 profile runner 和 HTTP 传输。
后果
Host 和 pnpm 启动时传入 --expose-internals:内置 Cordis 加载器使用 Node 内部 ESM 加载器,而其原生 builtin 访问器无法在 Electron 44 中找到所需符号。显式参数使加载器可用,无需修改 Cordis。RunAsNode fuse 保持启用。
Host 继承 Desktop 的 PATH(macOS 和 Linux 上合并了登录 shell 环境),不加入 Desktop 私有的 bin 目录,因此 PTC 和 agent shell 不会通过该目录解析内部启动器。DSH_DESKTOP_NODE_EXECUTABLE 仅为包安装注入。包脚本环境在 PATH 前添加一个小型 node shell 启动器,把参数转发给当前 Electron 可执行文件。这支持没有系统 Node 的 shell 生命周期脚本。Windows 上它是 node.cmd,并非替代的 node.exe;第三方代码若绕过 shell 直接启动名为 node 的可执行文件,必须使用 process.execPath 或提供自己的运行时。子进程继承 RunAsNode;worker 线程继承 Host 参数。Desktop 不模拟上游 OpenSSL 行为,也不自动重编译任意第三方原生扩展。
Electron 的 Node 补丁和原生 ABI 属于发布兼容性责任。打包原生 smoke 在 PATH 不含系统 Node 的情况下验证 pnpm shell 脚本,并验证 Windows shell 终端输出、Koffi、Sharp 和 HTML 转换。Host smoke 加载共享 Cordis 的外部插件,通过真实 Web 应用提供其路由。各平台仍需完成签名和已安装应用验收;Windows 结果不能证明 macOS 兼容性。Windows Token 签名串行执行并保留首次失败,阻止排队任务重复提交被拒绝的 PIN。
考虑过的替代方案
独立 Node 可执行文件可以让 Host 与 Electron 运行时分离,但会为该职责增加另一份二进制文件、下载、签名和版本选择。Electron RunAsNode 复用 GUI 可执行文件。生产包位于 ASAR 中,原生资源解包存放;打包与物理资源解析仍独立于执行引擎选择。