Deno 1.46: 2.0 前的最后一个版本

838 阅读13分钟

原文:Deno 官方博客

作者:Bartek Iwańczuk、Andy Jiang 2024 年 8 月 22 日

译者注:原文链接过多,已将大部分删除,有需要的读者请阅读原文


Deno 的目标是通过提供 原生 TypeScript 支持、一体化工具链、Web 标准 API 支持,以及 默认安全的代码执行 来提升 JavaScript 编写方式。

Deno 1.46(我们的最终 1.x 版本)继续朝着这一愿景迈进,通过 简化 CLI 的 更短的调用 和 简写的权限标志,实现 使用 deno serve --parallel 的多线程 Web 服务器,并添加了许多 Node.js/npm 兼容性改进(支持 playwright@google-cloudmysql2pglitessh2 等)。

除了这些突出的更新,1.46 还提供了 更好的依赖管理,包括 deno removedeno cleandeno compile 的代码签名 以提高软件的可移植性,以及 新增对 HTML、CSS、YAML 等的支持 的 deno fmt

要升级到 Deno 1.46,请在终端中运行以下命令:

deno upgrade

如果尚未安装 Deno,请运行以下命令之一进行安装,或在此处学习如何安装

curl -fsSL https://deno.land/install.sh | sh

iwr https://deno.land/install.ps1 -useb | iex

更简单的 CLI

此版本为 deno CLI 带来了重大更新。

更短的调用

此版本带来了长期请求和期待的变化,改变了运行 Deno 程序的方式。

denodeno run 被修改为提供更符合人机工程学的常用任务方式。

现在,你可以仅用 deno 运行程序,而不必指定 run 子命令:

# Deno v1.45
deno run hello.ts
Hello world 🦕

# Deno v1.46
deno hello.ts
Hello world 🦕

你仍然可以将所有必要的标志传递给 deno 子命令:

# Deno v1.45
deno run --allow-read main.ts
Reading ./log.txt...

# Deno v1.46
deno --allow-read hello.ts
Reading ./log.txt...

另一个符合人机工程学的变化是能够使用 deno run 运行任务:

// deno.json
{
  "tasks": {
    "test": "deno test --allow-read --allow-net"
  }
}
# Deno v1.45
deno task test

# Deno v1.46
deno run test

这可能是一个小变化,但肌肉记忆是实际存在的!对于有多年使用 npm run <script> 经验的用户,这应该是一个改变游戏规则的功能。

⚠️ 预 v1.45 行为仍然支持。 你仍然可以像以前一样使用 deno taskdeno run。这些变化旨在使熟悉其他脚本环境的用户更容易过渡,这些环境默认执行第一个参数作为脚本。

简写权限标志

Deno 的权限系统是 Deno 提供的最重要的功能之一。--allow-*--deny-* 标志用于限制对系统某些部分的访问。由于其冗长,很多用户选择使用 --allow-all 标志,或更准确地说是其简写 -A

在 Deno v1.46 中,你现在可以享受大多数常用权限标志的一字母变体:

长格式短格式
--allow-read-R
--allow-write-W
--allow-env-E
--allow-net-N
--allow-sys-S

这些简写标志与长格式一样接受允许列表 - 例如,-R=./static-E=DEBUG,LOG-N=deno.com

如果你不想提供允许列表,可以将它们组合在一起 - 例如,-RWN 将启用 readwritenet 权限。

以下是一些缩短 CLI 调用的示例:

# Deno v1.45
deno run --allow-read=./static --allow-env --allow-net=myapi.example.com main.ts

# Deno v1.46
deno run -R=./static -E -N=myapi.example.com main.ts

# Deno v1.46 无需 `run` 子命令
deno -R=./static -E -N=myapi.example.com main.ts
# Deno v1.45
deno run --allow-read --allow-write --allow-env --allow-sys main.ts

# Deno v1.46
deno run -RWES main.ts

# Deno v1.46 无需 `run` 子命令
deno -RWES main.ts

新的帮助输出

CLI 选项标志现在被逻辑地分组到不同的类别中:

权限提示链接到文档

权限提示现在链接到相关权限标志的文档:

deno help <subcommand>

--allow-* 标志的帮助输出

权限标志的帮助文本已刷新,包括示例用法。

--env 重命名为 --env-file

--env 标志提供了“dotenv”功能,可以从指定文件中读取环境变量,并在 JavaScript 代码执行之前设置它们。这是非常方便的,所有流行的 JS 运行时都提供了此功能。为了与其他运行时对齐并简化迁移,--env 标志已重命名为 --env-file。旧标志将继续有效,但不会在帮助输出中显示。

新的进度条

一些反馈指出,Deno 有时似乎在下载依赖项时卡住了。实际上,问题有很多种 - 网络连接差、包注册表的速率限制等等。尽管如此,缺乏交互式的工作指示并没有帮助那些急于开始编码的用户。

为了明确表示 Deno 仍在工作,我们刷新了进度条以提供更多交互性。

Deno 1.45:掘金放不了视频

Deno v1.46:掘金放不了视频

deno checkdeno cache 参数的 Glob 支持

Deno v1.34 添加了对配置文件和 CLI 标志中的 glob 的支持。这种 glob 功能自那时起已扩展到大多数 Deno 子命令,现在你可以在 deno checkdeno cache 子命令中享受 glob 扩展。

⚠️ 确保将 glob 放在引号中。 否则,你的 shell 将展开它而不是 Deno,这可能导致由于 glob 实现差异而出现微妙的错误。

# 类型检查当前目录及其子目录中的所有 `.ts` 文件
deno check "*/**/*.ts"

# 缓存当前目录中的所有 JS 文件
deno cache "*.js"

更快的 deno serve

deno serve 在 Deno v1.43 中引入,并收到了很多积极的反馈。这个版本为deno serve带来了一些备受期待的变更。

1. 开发

通过添加 deno init --serve 选项,你可以在 10 秒内启动一个服务器。 示例项目包括使用 @std/http 的路由和集成的文件服务器,以实现高效的静态资产服务。

$ deno init --serve
$ deno serve -R main.ts
Listening on http://localhost:8000
$ curl http://localhost:8000/hello

这个子命令旨在成为一个快速原型制作工具,随着你的项目的发展,你可以对它进行扩展,并且并非旨在替代像 Fresh 这样的框架。

2. 类型检查

deno serve 的一个限制是正确设置入口文件的类型检查的复杂性。有了 Deno v1.46 和最新的 TypeScript 特性,现在这变得简单了:只需在入口文件的 default export 添加 satisfies Deno.ServeDefaultExport

export default {
  fetch(req) {
    console.log(req.RequestDoesntHaveThisPropery);
    return new Response("Hello world!");
  },
} satisfies Deno.ServeDefaultExport;

如果你类型检查这个文件,Deno 会警告任何 API 兼容性问题:

$ deno check server.ts
Check file:///dev/server.ts
error: TS2339 [ERROR]: Property 'RequestDoesntHaveThisPropery' does not exist on type 'Request'.
    console.log(req.RequestDoesntHaveThisPropery);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at file:///dev/server.ts:3:21

注意:deno init --serve 会在你搭建新服务器项目时自动添加 satisfies Deno.ServeDefaultExport 到你的 deno serve 入口点。

3. 扩展

Deno.serve() API 为 deno serve 子命令提供动力,已经非常快速。但它默认只使用一个线程。这是有意义的,因为 JavaScript 是单线程语言,假设你没有使用像 new Worker() 这样的 API。但现代服务器有多个核心,应该利用这些核心以最有效地使用资源。

为了帮助你根据可用资源实现最佳性能,deno serve 现在支持一个 --parallel 标志,让你通过利用 CPU 的多个核心在多个线程上运行你的服务器:

$ deno serve --parallel main.ts
deno serve: Listening on http://0.0.0.0:8000 with 10 threads

你可以通过使用 DENO_JOBS 环境变量来指定要使用的线程数量。如果你没有指定这个变量,它将默认为你的机器提供的线程数量。例如,要将线程数量限制为 4,你可以运行:

$ DENO_JOBS=4 deno serve --parallel main.ts
deno serve: Listening on http://0.0.0.0:8000 with 4 threads

这里没有魔法 —— Deno 只是运行 X 份相同的服务器,保持每个线程为单线程 JavaScript 运行时。这使得你的应用程序状态的推理变得更简单,因为本地扩展的行为就像在云中扩展一样。

了解更多关于这个功能的工作方式,可以查看这个关于并行化 deno serve 的 Deno 演讲

deno fmt 现在支持 HTML、CSS、YAML 等

这个版本为 Deno 的内置格式化程序 deno fmt 带来了重大升级。

deno fmt 现在可以用来格式化 HTML、CSS、SCSS、Sass、Less、YAML、Astro、Angular、Svelte 和 Vue 文件。

让我们格式化这个 HTML 文件:

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
            <meta charset="UTF-8">
 <title>Hello `deno fmt`</title>
</head>
<body>
      <h1>Hello <pre>
          deno fmt!
      </pre></h1>
    </body></html>
$ deno fmt --unstable-html index.html
$ cat index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Hello `deno fmt`</title>
  </head>
  <body>
    <h1>Hello <pre>
          deno fmt!
      </pre></h1>
  </body>
</html>

由于这是 deno fmt 的一个重大变更,这些新格式的格式化目前是不稳定的。为了在 deno fmt 中启用这些新格式,需要传递 --unstable-* 标志或在 deno.json(c) 文件中添加选项。

请注意,当这些选项启用时,deno fmt 将自动拾取相应的文件。

使用 CLI:

# 格式化 HTML
$ deno fmt --unstable-html index.html

# 格式化 CSS/SCSS/Sass/Less
$ deno fmt --unstable-css styles.css

# 格式化 YAML
$ deno fmt --unstable-yaml config.yml

# 格式化组件文件
$ deno fmt --unstable-component App.vue App.svelte

使用配置文件:

// deno.json
{
  "unstable": [
    "fmt-html",
    "fmt-css",
    "fmt-yaml",
    "fmt-component"
  ]
}
$ deno fmt index.html styles.css config.yml App.vue App.svelte

非常感谢 Pig Fang 提供这些格式化程序!

导入断言已弃用

Deno 在 v1 中引入了导入断言提案,允许你导入 JSON 文件:

import data from "./data.json" assert { type: "json" };
console.log(data);

从那时起,该提案经历了重大变化,包括将关键字从 assert 更新为 with,并被重命名为导入属性。

在 Deno 2 中,将不再支持导入断言。为了帮助你迁移到导入属性,从 Deno v1.46 开始,每次使用 assert 关键字时都会打印警告,应该用 with 关键字替换:

// data.json
{
  "deno": "2"
}
$ deno run main.js
⚠️  Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.

import data from "./data.json" assert { type: "json" };

  at file:///dev/main.js:1:31

{ deno: "2" }

让我们将 assert 更改为 with

import data from "./data.json" with { type: "json" };
console.log(data);
$ deno run main.js
{ deno: "2" }

依赖管理

Deno v1.46 带来了几个依赖管理的改进:

deno add 现在支持 dist 标签并建议使用 npm

现在可以使用 dist 标签添加 npm 依赖,例如使用 deno add npm:ajv@latest 来获取 ajv 包的 latest 标签。

此外,当你尝试添加一个仅在 npm 上可用的 JSR 包时,deno add 现在会提供一条有用的消息:

$ deno add ajv
error: jsr:ajv was not found, but a matching npm package exists. Did you mean `deno add npm:ajv`?

deno remove

由于一项普遍的请求,deno 现在有一个子命令,可以从你的配置和锁文件中移除依赖:

$ deno remove @std/assert @std/path
Removed @std/assert
Removed @std/path

请记住,这些包仍将保留在全局缓存中以备将来使用。如果你想完全清理你的环境,请查看 deno clean

deno clean

Deno 有一个全局缓存,你可以使用 DENO_DIR 环境变量来控制。这个缓存用来存储远程依赖项以在多个项目中有效使用,用于更快启动的 V8 代码缓存,以及 deno fmtdeno lint 等子命令的缓存,以最大程度减少每次运行时的工作量。

你现在可以通过调用 deno clean 来快速清除整个缓存:视频演示

deno compile 添加代码签名

deno compile 生成的程序现在可以进行代码签名:

deno compile app.ts

# 在 macOS 上使用 `codesign` 工具
codesign --sign ./app

# 在 Windows 上使用 `signtool`
signtool sign /fd SHA256 .\app.exe

除此之外,为 Windows 编译的程序可以有一个自定义图标:

deno compile --icon=./icon.ico game.tsx

最后,调整了权限提示,以便在遇到缺少权限时提供更有用的错误信息:

console.log(Deno.readTextFileSync("./log.txt"));
# compile with no permissions
deno compile ./app.js

./app
Requires read access to ./log.txt, specify the required permissions during compilation using `deno compile --allow-read`

# compile again with suggested permissions
deno compile --allow-read ./app.js

./app
Hello world!

了解更多关于这个最新更新的信息,可以查看这个关于 deno compile 的 Deno 演讲

deno publish 帮助确保包是许可的

为了确保包获得正确许可,deno publish 现在在发布包时需要一个license字段或文件。

> deno publish
error[missing-license]: missing license field or file
 --> deno.json
  = hint: add a "license" field. Alternatively, add a LICENSE file to the package and ensure it is not ignored from being published

  docs: https://jsr.io/go/missing-license

error: Found 1 problem

要解决这个问题,向你的 deno.jsonjsr.json 文件添加一个 "license" 字段。

{
  "name": "@deno/add",
  "version": "1.0.0",
  "license": "MIT", // 添加这个或包含一个 LICENSE 文件
  "exports": "./mod.ts"
}

deno upgrade 改进

deno upgrade 是一个方便的方式,用来保持 Deno 的最新状态,或Canary版本。

这个版本让它更加方便,一旦你升级到 Deno v1.46.0,你可以省略任何 CLI 标志,改为使用:

# 升级到最新的稳定版本
$ deno upgrade

# 升级到特定版本
$ deno upgrade v1.46.0

# 升级到最新的金丝雀版本
$ deno upgrade canary

# 升级到特定的发布版本
$ deno upgrade 526f39fbb9dacce12c69d24a55798d0bde8f2707

此外,我们添加了一个新的发布通道:发布候选(Release Candidate)通道。我们计划在发布 Deno 2 之前提供几个发布候选项。

你可以通过运行 deno upgrade rc 来试用它,但请记住目前只有 v1.46.0 的发布候选项可用。

Node.js 和 npm 兼容性

这个版本带来了 Deno 对 Node.js 和 npm 包的兼容性的许多改进。这个周期专注于让几个被强烈要求的流行包工作:

  • playwright 现在在 Linux 和 macOS 上得到支持,这要归功于 node:child_process API 中改进的管道支持
  • 许多 node:crypto API 被重写,修复了许多错误并支持多个包(例如 ssh2):
    • 添加了 CipherIv.setAutoPadding() 修复了 ethereum-cryptography
    • 现在可用的 crypto.diffieHellman
    • crypto.randomFillSync() 尊重偏移量
    • 加密密钥的处理已经彻底改革,增加了对 PKCS#1、PKCS#8、RSA-PSS、X25519、ED25519、DH 等密钥的支持
    • 支持 ieee-p1363 ECDSA 签名
    • crypto.createPublicKey() 使用 PEM 私钥
    • ...
  • 由于对 Http2Session.socket 的支持,@google-cloud 包现在得到支持
  • npm:bindingsnpm:callsites 包由于 Error.prepareStackTrace API 的修复而正确工作
  • fsevent 模块现在得到支持
  • vitest 兼容性因以下原因而向前发展:
    • 现在可用的 node:inspector
    • node:inspector#Session 构造函数不会抛出异常
  • yoctocolors 现在通过修复 tty.WriteStream.hasColor 并支持 tty.hasColors()tty.getColorDepth() 正确工作
  • util.debug 现在得到支持
  • chokidar 由于 Windows 上的固定定位而更加可靠
  • mongodb 由于 net.Socket API 的修复和另一个在 node:http 中的修复而更加稳定
  • mysql2 包现在得到支持
  • node:vm 接收了另一个重大改革
  • size-limit 由于 node:zlib 模块的更好覆盖而正确工作
  • aws-sdk 由于更好的子数组处理而更加可靠
  • ecmarkup 现在正确工作
  • pglite 现在完全支持
  • grpc-js 应该由于释放流容量而更加可靠
  • paper-jsprismjs 通过修复暴露给 npm 包的全局变量而得到更好的支持
  • fs.existfs.existSync 已经优化
  • shelljs 现在使用 npm: 指定符工作
  • 更好的 Buffer 性能,当从字符串转换时

Node.js 和 npm 支持的其他变更包括:

  • .npmrc 文件现在支持 username_password 选项
  • 使用 Node.js global,而不导入它们会生成 lint 警告。提供免费的快速修复操作。

性能改进

Deno v1.46 带来了一些性能改进:

  • Deno.serve() API 现在快了 8-15%
  • crypto.randomUUID() 现在快了 5 倍
  • Response.clone() 快了 80%
  • deno doc 获得多项优化,以改善内存使用率

测试和覆盖率改进

这个版本为 deno testdeno coverage 带来了以下改进:

  • deno coverage --html 现在显示面包屑导航,便于导航

deno coverage HTML 报告中的面包屑导航

  • deno test 获得一个新的 --hide-stacktraces 标志,它将禁用在测试失败时打印错误堆栈跟踪
  • deno test 中的 --allow-none 标志已被重命名为 --permit-no-files。我们打算在 Deno 2 中弃用旧标志,所以如果你依赖它,请确保更新你的脚本!

更简洁的冻结锁文件

在 Deno v1.46 中引入的冻结锁文件,是一种简单方便的方式,以确保当新的依赖项被添加到锁文件时,Deno 会报错。

这个版本通过在配置文件中指定以下内容,使每个人都能更容易地使用这个选项:

{
  "lock": {
    "frozen": true
  }
}

每当更新将更改 lockfile 的依赖项时,Deno 都会抛出一个错误,要求你显式传递 --frozen=false 标志,这使得添加依赖项成为你有意识的决定。


此外,为了节省带宽和检查 git diff 所需的时间,lockfile格式在 Deno 2 中将变得更加简洁。

你今天就可以试用它,通过使用 DENO_FUTURE=1 环境变量。注意,切换回不使用 DENO_FUTURE 将需要重新生成锁文件。

文件监视器

在 Deno v1.38 中添加的热模块替换,现在在 --watch-hmr 标志下被认为是稳定的。它将显示在相关子命令的帮助输出中 --watch 标志旁边。

此外,deno test 得到了 --watch=<PATHS> 标志的支持,允许你传递其他要监视的文件路径,以触发重新运行测试。

Web API

Deno v1.46 带来了两个显著的 Web API 补充:

  • AsyncIterable<T> 现在可以用来构造 RequestResponse 类的主体。例如,node:fs 中的 createReadStream 是一个实现 AsyncIterable 协议的 API,所以它可以像这样与 fetch API 一起使用:
const file = createReadStream("./fixture.json");

const response = await fetch("http://example.com", {
  method: "POST",
  body: file,
});

Deno 现在将在数据变得可用时(从磁盘读取)流式传输数据,以实现高效的 HTTP 调用。

  • URLPattern API 现在支持 ignoreCasehasRegExpGroups。前者允许不区分大小写的模式匹配:
const pattern = new URLPattern({
  pathname: "/foo/bar",
}, {
  ignoreCase: true,
});
pattern.test("/foo/bar"); // true
pattern.test("/fOo/BaR"); // true

hasRegExpGroups 属性让你知道模式是否使用任何正则表达式组:

const pattern = new URLPattern({
  pathname: "/foo/bar/(.+)",
});
pattern.hasRegExpGroups; // true

标准库稳定

Deno 标准库提供了一组从数据解析和操作、使用 Web 协议、以及一般实用程序的高质量包,这些包由核心团队审核,并保证与 Deno 兼容。

我们很高兴地宣布,我们已经完成了我们的稳定化过程,有 30 个包达到了 v1 或更高版本。

  1. std/assert
  2. std/async
  3. std/bytes
  4. std/cli
  5. std/collections
  6. std/crypto
  7. std/csv
  8. std/data-structures
  9. std/encoding
  10. std/expect
  11. std/fmt
  12. std/front-matter
  13. std/fs
  14. std/html
  15. std/http
  16. std/json
  17. std/jsonc
  18. std/media-types
  19. std/msgpack
  20. std/net
  21. std/path
  22. std/regexp
  23. std/semver
  24. std/streams
  25. std/testing
  26. std/text
  27. std/toml
  28. std/ulid
  29. std/uuid
  30. std/yaml

为了分享使用标准库模块可以完成的事情,我们发布了一个推文线程,每条推文都有一个模块的代码示例。

如果你想了解有关我们的标准库的更多信息,请查看我们短暂的 3 分钟视频

V8 12.9

Deno 1.46 配备了最新的 V8 12.9。

致谢

没有我们社区的帮助,我们无法构建 Deno!无论是在我们社区 Discord 服务器回答问题,还是报告错误,我们都非常感谢你的支持。特别感谢下面这些人对 Deno 1.46 的贡献:Andreas Deininger、Bedis Nbiba、Birk Skyum、Hajime-san、HasanAlrimawi、Ian Bull、Igor Borisoglebski、Ivancing、Kenta Moriuchi、Kyle Kelley、MrEconomical、MujahedSafaa、Pig Fang、Rano | Ranadeep、Roy Ivy III、Sean McArthur、Yazan AbdAl-Rahman、Zebreus、chirsz、i-api、melbourne2991、seb、vwh,和 Łukasz Czerniawski。

你想加入 Deno 贡献者的行列吗?在这里查看我们的贡献文档,下次我们会在名单上见到你。

信不信由你,上面列出的更改仍然没有告诉你 1.46 中所有的改进。你可以在 GitHub 上查看合并到 Deno 1.46 的所有拉取请求的完整列表。

感谢你关注我们的 1.46 版本发布,我们希望你喜欢使用 Deno 构建!

接下来是什么?

🚨️ Deno 2 即将到来 🚨️

几天后,Deno 2 的第一个“发布候选版本”将被发布。

Deno 2 中有一些小的破坏性变更。

你可以使用 deno upgrade rc 来获取它,但你今天就可以添加 DENO_FUTURE=1 环境变量来为你的代码提供未来的保障。