原文:Deno 官方博客
作者:Bartek Iwańczuk 等 2024 年 7 月 11 日
Deno继续演进到了1.45版本。本次更新的亮点是引入了工作空间(workspace),为管理单库存储提供了强大的解决方案。此次更新简化了大型代码库中的依赖管理、配置共享和模块组织。除了工作空间,此更新还改进了与Node.js的兼容性,更新了 deno install,新增了 deno init --lib 命令,废弃了 deno vendor 等功能。
要升级到 Deno 1.45,请在终端中运行以下命令:
deno upgrade
如果尚未安装 Deno,请运行以下命令进行安装或在此处了解安装方法。
# Using Homebrew (macOS):
brew install deno
# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/install.sh | sh
# Using PowerShell (Windows):
iwr https://deno.land/install.ps1 -useb | iex
工作空间支持
Deno v1.45 增加了对工作空间和单库存储的支持。支持的工作空间有两种形式:在 deno.json 中定义的 Deno-first 工作空间和向后兼容的 npm 工作空间。
Deno 工作空间非常直观。全局定义的配置会应用到每个成员包,但可以被成员自行覆盖。你可以混合匹配 npm 和 Deno 工作空间——在 Deno 工作空间内包含一个 npm 包,反之亦然。
将工作空间成员发布到 JSR 只需运行 deno publish。标准库 是如何有效使用工作空间的良好示例。
开始使用时,在你的 deno.json 内定义一个 "workspace" 元素,并列出成员目录。
{
"workspace": ["./add", "./subtract"]
}
npm 工作空间 在 Deno 中也能无缝使用。无论是在较大的 npm 工作空间中包含 Deno 库,还是在较大的 Deno 工作空间中包含 npm 库,依赖关系都能正确解析。
要了解更多关于工作空间支持的信息,请访问 Deno 文档。
Node.js 兼容性改进
Node-API 支持得到了完全改进,修复了 prisma、sqlite3、paper、duckdb、nodejs-polars 等包存在的许多问题。
其他 Node.js 兼容性改进包括:
- 我们正在努力支持
dd-trace。尽管还未完全支持,但我们已经通过以下方式逐步接近:- 添加了
net.BlockList和net.SocketAddress - 向
node:diagnostics_channel模块添加了缺失的 API - 支持
Module.parent
- 添加了
- 现在支持
fs.lutimes和fs.lutimesSync,以及fs.lchown和process.getegid - 在
node:crypto和node:zlib中添加了常量 - 在
node:fs中,现在可用Dirent.path和Dirent.parentPath node:http模块获得了重大更新:Server#close()现在执行优雅关闭,允许未完成的请求完成- 添加了
ServerResponse#appendHeader()功能 - 现在支持所有
ServerResponse#writeHead()签名 ServerResponse#setHeaders现在可以处理头部数组ServerResponse正确处理分块写入ServerResponse在流式传输时不会崩溃Server如果尚未开始监听,则为address返回nullClientRequest现在正确发送请求
node:vm将消耗更少内存crypto.Hash实现得到了改进@grpc/grpc-js支持现在更加健壮,正确设置了end_stream标志node:child_processAPI 现在支持stdio选项的"ipc"值- Buffer 实现已经优化,两次
readline/promises模块现在在 ES 模块中可用
此外,还涉及与npm支持相关的其他改进:
- Deno 将发现放置在你的主目录中的
.npmrc配置文件,以更好地支持私有注册表 - 随包一起提供的类型现在优先于
@types作用域中的类型 - Deno 现在支持更多形式的
SemVer约束
冻结锁文件(lockfile)
新增了一个 --frozen(别名 --frozen-lockfile)标志,用于控制锁文件的行为。
你可以使用这个标志来让 Deno 在锁文件过期时报错。这在 CI 管道中尤其有用,你需要确保所有提交的代码都是最新的,并且没有对依赖项的意外更改。
在运行带有 --frozen 标志的 deno 命令时,任何试图用新内容更新锁文件的操作都会导致命令退出,并显示本应进行的修改。
例如,假设在项目的某处你正在导入 npm:chalk@5.3.0。然后,稍后某人从一个稍微过时的 AI 聊天机器人中复制了一段只知道 chalk 版本 5.2.0 的代码片段,所以某个文件中出现了类似以下的导入:
import chalk from "npm:chalk@5.2.0";
幸运的是,你的 CI 管道在测试步骤中指定了 --frozen:
deno test --frozen --coverage
这样,它不会悄悄地将第二个(过时的)版本的 chalk 添加到你的依赖树中,而是会失败,并显示 chalk 5.3.0 本应被添加到你的锁文件中:
error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it.
changes:
5 | - "npm:chalk@5.3.0": "npm:chalk@5.3.0"
6 | - },
7 | - "npm": {
5 | + "npm:chalk@5.2.0": "npm:chalk@5.2.0",
6 | + "npm:chalk@5.3.0": "npm:chalk@5.3.0"
7 | + },
8 | + "npm": {
9 | + "chalk@5.2.0": {
10 | + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
11 | + "dependencies": {}
12 | + },
如果你打算明确地更新你的锁文件,可以指定 --frozen=false,这将更新锁文件而不会报错。
使用上述设置,每次依赖项更新都需要运行带有 --frozen=false 标志的命令或任务,从而使这些更新变得有意且明确。
最后,--lock-write 现在已被弃用,并将在 Deno 2 中删除。你可以用 --frozen=false 替代 --lock-write 的使用。
deno install 更新
在 Deno 2 中,deno install 子命令将更像 npm install 以支持常见的工作流程。
目前在 Deno 中,deno install <pkg> 全局安装一个二进制包。在 Deno 2 中,deno install <pkg> 默认会本地安装一个包,向项目添加一个新的依赖项(类似于 deno add <pkg>),并进行缓存。
此外,不带参数的 deno install 将缓存 package.json 或导入映射中列出的本地依赖项,并设置一个本地 node_modules 目录(如果适用)。
要全局安装,你需要指定 --global (-g) 标志:deno install --global <pkg>。
要在你的项目中试用新的 install 命令,请运行:
DENO_FUTURE=1 deno install
带参数的情况,如 deno add:
DENO_FUTURE=1 deno install @david/dax
我们鼓励你试用新的 deno install 并报告遇到的任何问题!
npm 生命周期脚本支持
package.json 中的某些脚本是特殊的,它们在某些操作时由 npm 自动执行。有许多生命周期脚本受 npm 支持,但作为包的使用者,主要相关的脚本是 pre/post install 脚本,它们在安装包时(即通常在 npm install 期间)执行。
一些包依赖于它们的安装脚本来执行设置步骤(例如,下载或构建原生插件的工件),如果不执行这些脚本,它们将无法正常工作。以前,Deno 不支持执行生命周期脚本,因此在使用某些包时可能会导致混乱的错误,而且没有简单的解决方案。
现在,Deno 支持在 deno cache(和 DENO_FUTURE=1 deno install)中运行生命周期脚本,并在检测到包具有未运行的生命周期脚本时发出警告。
通过在 deno cache(或 DENO_FUTURE=1 deno install)中指定 --allow-scripts 标志,你可以选择运行特定包的生命周期脚本:
deno cache --allow-scripts=npm:duckdb main.ts
ℹ️ 注意
目前我们仅在使用本地
node_modules目录时支持生命周期脚本(deno.json中的"nodeModulesDir": true)。未来我们计划添加不使用
node_modules时的生命周期脚本支持,但这将是尽力而为,因为某些包依赖于在node_modules目录中。
deno init --lib - 轻松设置一个新的库
deno init 子命令在 Deno v1.25 中引入,允许你通过几次按键启动一个最小的 Deno 项目脚手架。
自从引入 JSR 以来,用户一直在要求一种快速启动将发布到 JSR 的项目的方法。
在 Deno v1.45 和 deno init --lib 中,你可以轻松做到这一点:
✅ Project initialized
Run these commands to get started
# Run the tests
deno test
# Run the tests and watch for file changes
deno task dev
# Publish to JSR (dry run)
deno publish --dry-run
在发布之前,请确保更新生成的 deno.json 文件中的 name 和 version 字段!
deno vendor 现已弃用
deno vendor 在 Deno v1.19 中添加,允许用户在项目目录内供应所有依赖项。
从那时起,在 Deno v1.37 中引入了另一种供应依赖项的方法使用 --vendor 标志或配置文件中的 { "vendor": true } 选项。
这个选项收到了很多积极反馈,并指出了 deno vendor 子命令的缺点和较差的开发者体验。考虑到这些因素,deno vendor 现已弃用,计划在 Deno 2 中删除。
请迁移到使用 --vendor 标志或配置文件中的 vendor 选项。
deno test 文件发现
deno test 可以自动发现并运行符合某些模式的测试文件:
- 文件名以
_test结尾 - 例如app_test.ts,component_test.tsx - 文件名以
.test结尾 - 例如router.test.ts,controller.test.js - 文件名为
test- 例如test.ts,test.js
为了提高与更广泛生态系统的兼容性,deno test 现在将自动发现并运行 __tests__ 目录下的文件:
$ tree
.
├── __tests__
│ ├── integration.ts
│ └── unit.ts
└── main.ts
2 个目录, 3 个文件
在 Deno v1.44 中:
$ deno test
error: No test modules found
在 Deno v1.45 中:
$ deno test
deno test
Check file:///Users/ib/dev/test_discovery/__tests__/integration.ts
Check file:///Users/ib/dev/test_discovery/__tests__/unit.ts
running 1 test from ./__tests__/integration.ts
integration test ... ok (0ms)
running 1 test from ./__tests__/unit.ts
unit test ... ok (0ms)
ok | 2 passed | 0 failed (11ms)
Blob.bytes()
继 Deno v1.44 的更改和 Web 文件 API 规范的更新之后,现在支持 Blob.bytes():
const jsonStr = JSON.stringify({ hello: "world" }, null, 2);
const blob = new Blob([jsonStr], { type: "application/json" });
const buffer = new Uint8Array(await blob.arrayBuffer());
const blob = new Blob([jsonStr], { type: "application/json" });
const buffer = await blob.bytes();
Jupyter 笔记本的改进
你现在可以在 Jupyter 笔记本中使用 prompt 和 confirm API,以提供更多的灵活性和互动性。视频详见此处。
为了进一步改进 JavaScript(和 TypeScript)的数据科学生态系统,我们计划在下一个版本中添加使用 JSX 和 React 的交互式小部件的支持。
deno compile 支持 --env 标志
--env 标志 在 Deno v1.38 中添加,为从 .env 文件加载进程环境变量提供了原生支持。
在 Deno v1.45 中,可以使用 --env 标志将某些环境变量嵌入使用 deno compile 生成的二进制文件中。
🛑 注意
请记住,在检查已发布程序的内容时,这些环境变量仍然可以被读取,所以请谨慎使用此功能。将生产密钥写入二进制文件可能不是一个好主意。
在使用 deno compile 创建的程序执行时,所有对 Deno.env.get(<var_name>) 的调用将返回在编译过程中指定的值,而不是用户系统上的当前变量:
$ cat .env
HELLO_THERE=deno
$ cat main.ts
console.log("Hello there");
console.log(Deno.env.get("HELLO_THERE") + "!");
$ deno compile --env --allow-env main.ts
...
在 Deno v1.44.4 中
HELLO_THERE="General Kenobi" ./main
Hello there
General Kenobi!
在 Deno v1.45.0 中
HELLO_THERE="General Kenobi" ./main
Hello there
Deno!
更灵活的语言服务器
之前,VSCode 扩展只能读取和合并位于工作区根目录的 deno.json 或 deno.jsonc 文件。这些配置会应用于每一个打开的源文件。这使得某些 monorepo 配置变得不可能实现。
1.45 使得语言服务器更独立于编辑器中打开的根文件夹。现在,即使在子目录中有多个配置文件,它们也会被检测到。每个被发现的 deno.json 或 deno.jsonc 都会产生一个独立的作用域,拥有自己类型检查环境、模块解析等。你可以为 compilerOptions.libs 配置不同的条目,或者导入模块以增强全局类型,这些都不会污染其他 deno.json[c] 作用域的环境。
标准库更接近稳定
Deno 标准库提供了一套由核心团队审核并保证与 Deno 兼容的高质量包。 如我们之前的 博客文章 所提到的,标准库目前正在进行稳定化工作,目标是使 38 个包中的 31 个达到稳定状态。
到目前为止,我们已经稳定了 13 个包:
@std/assert@std/bytes@std/collections@std/crypto@std/data-structures@std/encoding@std/html@std/media-types@std/msgpack@std/path@std/regexp@std/toml@std/uuid
这些包已达到 1.0.0 版本,遵循 SemVer 语义,并确保在其 1.x.x 版本中保持兼容性。
其余 18 个包已发布候选版本(RC)。如果你正在使用这些包中的任何一个,请考虑测试候选版本并 向我们反馈!
有关稳定化时间表的更多详细信息,请参阅 路线图问题。
V8 12.7 和 TypeScript 5.5.2
Deno 1.45 附带 V8 12.7 和 TypeScript 5.5.2。
使用 DENO_FUTURE=1 试用 Deno 2 的功能
我们鼓励你尝试使用 DENO_FUTURE=1 环境变量运行现有项目,这将启用 Deno 2 的功能。我们预计迁移工作量很小到非常小。如果你的体验不同,请与我们分享。
致谢
没有社区的帮助,我们无法构建 Deno!无论是通过在我们的社区 Discord 服务器上回答问题,还是报告错误,我们都非常感谢你的支持。特别感谢以下人员对 Deno 1.45 的贡献:Adam Gregory, Andreas Kohn, Andrew Johnston, Filip Skokan, HasanAlrimawi, Kenta Moriuchi, Luca Bruno, Oliver Medhurst, Richard Carson, Tom Alcorn, Victor Turansky, Yazan AbdAl-Rahman, Zander Hill, Zebreus, muddlebee, safaa-mojahed, ud2。
想加入 Deno 贡献者的行列吗?查看我们的贡献文档,下次我们将看到你的名字在列表中。
信不信由你,上面列出的更改仍然没有涵盖 Deno 1.45 中的所有改进。你可以在 GitHub 上查看 Deno 1.45 中合并的所有拉取请求的完整列表。
感谢你了解我们 1.45 版本的更新,希望你喜欢使用 Deno 构建项目!