Deno 1.45发布:Workspace和Monorepo支持

155 阅读10分钟

原文: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 支持得到了完全改进,修复了 prismasqlite3paperduckdbnodejs-polars 等包存在的许多问题。

其他 Node.js 兼容性改进包括:

  • 我们正在努力支持dd-trace。尽管还未完全支持,但我们已经通过以下方式逐步接近:
    • 添加了 net.BlockListnet.SocketAddress
    • node:diagnostics_channel 模块添加了缺失的 API
    • 支持 Module.parent
  • 现在支持 fs.lutimesfs.lutimesSync,以及 fs.lchownprocess.getegid
  • node:cryptonode:zlib 中添加了常量
  • node:fs 中,现在可用 Dirent.pathDirent.parentPath
  • node:http 模块获得了重大更新:
    • Server#close()现在执行优雅关闭,允许未完成的请求完成
    • 添加了 ServerResponse#appendHeader()功能
    • 现在支持所有 ServerResponse#writeHead()签名
    • ServerResponse#setHeaders现在可以处理头部数组
    • ServerResponse正确处理分块写入
    • ServerResponse在流式传输时不会崩溃
    • Server如果尚未开始监听,则为address返回null
    • ClientRequest现在正确发送请求
  • 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 文件中的 nameversion 字段!

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.tscomponent_test.tsx
  • 文件名以 .test 结尾 - 例如 router.test.tscontroller.test.js
  • 文件名为 test - 例如 test.tstest.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 笔记本中使用 promptconfirm 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.jsondeno.jsonc 文件。这些配置会应用于每一个打开的源文件。这使得某些 monorepo 配置变得不可能实现。

1.45 使得语言服务器更独立于编辑器中打开的根文件夹。现在,即使在子目录中有多个配置文件,它们也会被检测到。每个被发现的 deno.jsondeno.jsonc 都会产生一个独立的作用域,拥有自己类型检查环境、模块解析等。你可以为 compilerOptions.libs 配置不同的条目,或者导入模块以增强全局类型,这些都不会污染其他 deno.json[c] 作用域的环境。

标准库更接近稳定

Deno 标准库提供了一套由核心团队审核并保证与 Deno 兼容的高质量包。 如我们之前的 博客文章 所提到的,标准库目前正在进行稳定化工作,目标是使 38 个包中的 31 个达到稳定状态。

到目前为止,我们已经稳定了 13 个包:

  1. @std/assert
  2. @std/bytes
  3. @std/collections
  4. @std/crypto
  5. @std/data-structures
  6. @std/encoding
  7. @std/html
  8. @std/media-types
  9. @std/msgpack
  10. @std/path
  11. @std/regexp
  12. @std/toml
  13. @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 构建项目!