使用 Deno 如何将 Node.js 库转换为 Deno 库

原文:
www.edgedb.com/blog/how-we…
作者:James Clarke
发布时间:MAY 26, 2022
文章首发于知乎
zhuanlan.zhihu.com/p/524296632 著作权归作者所有 。商业转载请联系作者获得授权,非商业转载请注明出处 。
Deno 是新出的 JAVAScript 的运行时,默认支持 TypeScript 而不需要编译 。Deno 的创作者 Ryan Dahl,同样也是 Node.js 的创作者,解决了很多 Node 的基础设计问题和安全漏洞,更好的支持了 ESM 和 TypeScript 。
【使用 Deno 如何将 Node.js 库转换为 Deno 库】在 EdgeDB 中,我们开发了 Node.js 下的 EdgeDB client 库 ,并且在 NPM 中发布 。然而 Deno 有完全不同的另一套依赖管理机制,imports URL 地址 来引入 在 deno.land/x 上发布的包 。我们想找到一条简单的路 “Denoify” 代码库,可以将现有的 Node.js 包 生成 Deno 可以使用的包 。这样可以减轻维护的复杂度 。
Node.js vs DenoNode.js 和 Deno 的运行时有几个关键的区别,我们在调整使用 Node.js 编写的库时必须考虑到:

  1. TypeScript 支持:Deno 可以直接执行 TypeScript 文件,Node.js 只能运行 JavaScript 代码 。
  2. 模块解析:默认 Node.js 支持 CommonJS 规范的模块引入,使用 require/module.exports 语法 。同时 Node.js 有一个复杂的依赖解析算法,当 从 node_modules 中加载像 react 这样的模块时,对于 react 导出的内容会自动加后缀,比如说增加 .js 或 .json,如果 import 是目录的话,将会直接查找目录下的 index.js 文件 。Deno 极大简化了这一过程 。Deno 支持 ESM 模块规范,支持 import /export 语法 。同时 TypeScript 同样支持这一语法 。所有的引入如果不是一个相对路径包含显示文件扩展名就是一个 URL 路径 。这表明 Deno 不需要 node_modules 文件或是如 npm 或是 yarn 之类的包管理工具 。外部包通过 URL 路径直接导入,如 deno.land/x 或 GitHub 。
  3. 标准库:Node.js 有一套内置的标准模块,如 fs path crypto http 等 。这些模块可以直接通过 require(’fs’) 导入 。Deno 的标准库是通过 URL deno.land/std/ 导入 。两个标准库的功能也不同,Deno 放弃了一些旧的或过时的 Node.js api,引入了一个新的标准库(受 Go 的启发),统一支持现代 JavaScript 特性,如 Promises (而许多 Node.js api 仍然依赖于旧的回调风格) 。
  4. 内置的全局变量:Deno 将核心 API 都封装在 Deno 变量下,除了这之外 就没有其他暴露的全局变量,没有 Node.js 里的 Buffer 和 process 变量 。
因此,我们如何才能解决这些差异,并让我们的 Node.js 库尽可能轻松运行在 Deno ?让我们逐一分析这些变化 。
TypeScript 和模块语法幸运的是,我们不需要太担心将 CommonJS require/module 语法转换为 ESM import/export 。我们完全用 TypeScript 编写 edgedb-js,它已经使用了 ESM 语法 。在编译过程中,tsc 利用CommonJS 语法将我们的文件转换成普通的 JavaScript 文件 。Node.js 可以直接使用编译后的文件 。
这篇文章的其余部分将讨论如何将 TypeScript 源文件修改为 Deno 可以直接使用的格式 。
依赖幸运的是 edgedb-js 没有任何第三方依赖,所以我们不必担心任何外部库的 Deno 兼容性 。然而,我们需要将所有从 Node.js 标准库中导入的文件(例如 path, fs 等)替换为 Deno 等价文件 。
??注意:如果你的程序依赖于外部的包,需要到 deno.land/x 中检查是否有 Deno 的版本 。如果有继续向下阅读 。如果没有你需要和库作者一起努力,将包改为 Deno 的版本 。
由于 Deno 标准库提供了 Node.js 兼容模块,这个任务变得更加简单 。这个模块在 Deno 的标准库上提供了一个包装器,它试图尽可能忠实地遵守 Node 的 API 。
- import * as crypto from "crypto";+ import * as crypto from "<https://deno.land/std@0.114.0/node/crypto.ts>";复制代码为了简化流程,我们将所有引入的 Node.js API 包装到 adapter.node.ts 文件中,然后重新导出
// adapter.node.tsimport * as path from "path";import * as util from "util";import * as crypto from "crypto";export {path, net, crypto};复制代码同样 我们在 Deno 中使用相同的方式 adapter.deno.ts
// adapter.deno.tsimport * as crypto from "<https://deno.land/std@0.114.0/node/crypto.ts>";import path from "<https://deno.land/std@0.114.0/node/path.ts>";import util from "<https://deno.land/std@0.114.0/node/util.ts>";export {path, util, crypto};复制代码当我们需要 Node.js 特定的功能时,我们直接从 adapter.node.ts 导入 。通过这种方式,我们可以通过简单地将所有 adapter.node.ts 导入重写为 adapter.deno.ts 来使 edgedb-js 兼容 deno 。只要这些文件重新导出相同的功能,一切都应该按预期工作 。


推荐阅读