Deno 2.2:OpenTelemetry、Lint 插件、node:sqlite
要升级到 Deno 2.2,请在您的终端中运行以下命令
deno upgrade
如果 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 2.2 有哪些新功能
此版本中包含大量内容。以下是快速概述,可帮助您深入了解您最关心的内容
- 内置 OpenTelemetry
- Linter 更新
- 支持
node:sqlite
deno check
的改进deno lsp
的改进deno task
的实用更新- 依赖管理
Deno.cwd()
的权限检查放宽- 更小、更快的
deno compile
- 更精确的
deno bench
WebTransport
和 QUIC API- Node.js 和 npm 兼容性改进
- 性能改进
- WebGPU 的改进
- 更小的 Linux 二进制文件
- TypeScript 5.7 和 V8 13.4
- 长期支持
- 致谢
您还可以在v2.2 演示视频中查看所有这些功能的演示。
内置 OpenTelemetry 集成
Deno 2.2 内置了OpenTelemetry,用于监控日志、指标和跟踪。
Deno 自动监测诸如 console.log
、Deno.serve
和 fetch
等 API。您还可以使用 npm:@opentelemetry/api
来监测自己的代码。
让我们看看 Deno.serve
API 的一些日志和跟踪
Deno.serve((req) => {
console.log("Received request for", req.url);
return new Response("Hello world");
});
要捕获可观测性数据,您需要提供一个 OTLP 端点。如果您已经设置了可观测性系统,则可以使用它。如果没有,最简单的运行方式是在 Docker 中启动本地 LGTM 堆栈
$ docker run --name lgtm -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti \
-v "$PWD"/lgtm/grafana:/data/grafana \
-v "$PWD"/lgtm/prometheus:/data/prometheus \
-v "$PWD"/lgtm/loki:/data/loki \
-e GF_PATHS_DATA=/data/grafana \
docker.io/grafana/otel-lgtm:0.8.1
现在我们准备好运行服务器并捕获一些数据
OpenTelemetry 集成的 API 仍可能更改。因此,它被指定为“不稳定 API”,需要使用
--unstable-otel
标志才能使用。
$ OTEL_DENO=true deno run --unstable-otel --allow-net server.ts
Listening on http://localhost:8000/
现在,使用浏览器或 curl
连接到我们的服务器
$ curl http://localhost:8000
Hello world
您现在可以在您的可观测性系统中查看日志和跟踪。如果您使用 LGTM 堆栈,可以在http://localhost:3000访问 Grafana 仪表板。
Deno.serve()
API 处理的请求跟踪我们在这里只是触及了皮毛。Deno 还导出了自动监测的指标,您可以使用 npm:@opentelemetry/api
包创建自己的指标和跟踪范围。要了解更多信息,请访问Deno 文档。
您可以在此 v2.2 演示视频中观看 OpenTelemetry 集成的演示
Linter 更新
Deno 2.2 对deno lint
进行了重大升级,包括新的插件系统和 15 条新规则,特别适用于 React 和 Preact 用户。
新增内置 lint 规则
此版本增加了新的 lint 规则,主要针对 JSX 和 React 的最佳实践。
- jsx-boolean-value
- jsx-button-has-type
- jsx-curly-braces
- jsx-key
- jsx-no-children-prop
- jsx-no-comment-text-nodes
- jsx-no-duplicate-props
- jsx-no-unescaped-entities
- jsx-no-useless-fragment
- jsx-props-no-spread-multi
- jsx-void-dom-elements-no-children
- no-useless-rename
- react-no-danger-with-children
- react-no-danger
- react-rules-of-hooks
为了补充这些规则,新增了两个标签:jsx
和 react
。
请在Deno 文档中查看可用 lint 规则和标签的完整列表。
JavaScript 插件 API
deno lint
最大的更新是可以通过新的插件系统扩展其功能。
注意:插件 API 仍处于其 API 可能发生变化的阶段,因此目前被标记为不稳定功能。
虽然有许多内置规则,但在某些情况下,您可能需要针对特定项目量身定制的规则。
该插件 API 以ESLint 插件 API为模型,但并非 100% 兼容。实际上,我们预计一些现有的 ESLint 插件可以与 deno lint
无障碍地工作。
这是一个简单 lint 插件的示例。我们将创建一个插件,如果变量名为 foo
,则报告错误
{
"lint": {
"plugins": ["./my-plugin.ts"]
}
}
export default {
name: "my-lint-plugin",
rules: {
"my-lint-rule": {
create(context) {
return {
VariableDeclarator(node) {
if (node.id.type === "Identifier" && node.id.name === "foo") {
context.report({
node,
message: "Use more descriptive name than `foo`",
});
}
},
};
},
},
},
} satisfies Deno.lint.Plugin;
const foo = "foo";
console.log(foo);
$ deno lint main.js
error[my-lint-plugin/my-lint-rule]: Use more descriptive name than `foo`
--> /dev/main.js:1:7
|
1 | const foo = "foo";
| ^^^^^^^^^^^
Found 1 problem
Checked 1 file
除了基于访问者的 API 之外,您还可以使用类似 CSS 的选择器来定位特定节点。让我们使用选择器语法重写上述规则。
export default {
name: "my-lint-plugin",
rules: {
"my-lint-rule": {
create(context) {
return {
'VariableDeclarator[id.name="foo"]'(node) {
context.report({
node,
message: "Use more descriptive name than `foo`",
});
},
};
},
},
},
} satisfies Deno.lint.Plugin;
Lint 插件可以用 TypeScript 编写,Deno 在 Deno.lint
命名空间下开箱即用地提供了完整的类型声明。
您可以使用本地 lint 插件,以及来自 npm 和 JSR 的插件
{
"lint": {
"plugins": [
"./my-plugin.ts",
"jsr:@my-scope/lint-plugin",
"npm:@my-scope/other-plugin"
]
}
}
在Deno 文档中阅读更多关于 deno lint
插件 API 的信息。
deno lint
的 --rules
标志行为更新
此版本中,deno lint --rules
已更改为始终打印所有可用的 lint 规则,并标记当前配置中启用了哪些规则。
此外,deno lint --rules --json
不再打印原始 Markdown 文档,而是链接到Deno 文档中的相关规则页面。
您可以在此 v2.2 演示视频中观看 lint 插件 API 的更详细演示
deno check
的改进
Deno 用于类型检查的工具deno check
在此版本中获得了两项重大改进
- JSDoc 标签现在受到尊重
compilerOptions
的设置现在可以按工作区成员配置
让我们详细看看每个方面
@import
标签现在受到尊重
JSDoc 类型检查时,@import
JSDoc 标签现在受到尊重。这允许您内联定义导入,从而改进 JavaScript 文件中的类型检查。
export function add(a: number, b: number): number {
return a + b;
}
/** @import { add } from "./add.ts" */
/**
* @param {typeof add} value
*/
export function addHere(value) {
return value(1, 2);
}
addHere("");
$ deno check main.js
Check file:///main.js
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type '(a: number, b: number) => number'.
addHere("");
~~
at file:///main.js:10:9
compilerOptions
设置
工作区范围的 以前,deno.json
将相同的 compilerOptions
应用于所有工作区成员,使得前端和后端难以单独配置。现在,工作区成员可以定义自己的设置。
由于新增的对每个工作区成员的 compilerOptions 支持,现在可以在前端代码的目录中指定不同的 compilerOptions.lib
设置。
{
"workspace": [
"./server",
"./client"
],
"compilerOptions": {
"checkJs": true
}
}
{
"compilerOptions": {
"lib": ["dom", "esnext"]
}
}
document.body.onload = () => {
const div = document.createElement("div");
document.body.appendChild(div);
document.body.appendChild("not a DOM element");
};
$ deno check client/main.js
Check file:///client/main.js
TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'Node'.
document.body.appendChild("not a DOM node");
~~~~~~~~~~~~~~~~
at file:///client/main.js:4:29
error: Type checking failed.
您可以在此 v2.2 演示视频中观看 deno check
更新的演示
deno lsp
的改进
Deno 2.2 使deno lsp
更快、响应更及时,并为 Web 框架用户带来了重大改进。
这里无法详细介绍,但让我们看看一些亮点
- 将自动补全建议的速度提高 5-20 倍
- 处理阻塞代码中的取消请求
- 支持
compilerOptions.rootDirs
和compilerOptions.types
,为 Svelte、Qwik 和 Vite 用户提供更好的开发体验 - 正确识别环境模块导入
- 现在支持通配符模块增强(例如
.*css
和 Vite 虚拟模块) .wasm
文件的导入补全.scss
、.sass
、.less
、.sql
、.svelte
、.vue
和其他组件文件的格式化- 为内置 Node.js 模块的自动导入添加
node:
前缀 - 更好地处理
<reference types>
指令以及 npm 包对ImportMeta
接口的增强 - npm 包的自动导入功能更好
deno task
的实用更新
此版本为deno task
带来了一些更新。首先将帮助 deno task
变得更健壮和可预测
还有两项功能使 deno task
更实用、更方便。我们将详细介绍这些功能
- 任务名称中的通配符
- 运行无命令的任务
任务名称中的通配符
您现在可以在任务名称中使用通配符运行 deno task
,如下所示
{
"tasks": {
"start-client": "echo 'client started'",
"start-server": "echo 'server started'"
}
}
$ deno task "start-*"
Task start-client echo 'client started'
client started
Task start-server echo 'server started'
server started
请务必用引号将包含通配符的任务名称括起来,否则您的 shell 将尝试扩展此字符,您将遇到错误。
通配符字符(*
)可以放置在任何位置以匹配任务名称。所有匹配通配符的任务都将并行运行。
不带命令运行任务
任务依赖在v2.1中变得流行。现在,您可以通过定义不带命令的任务来更轻松地分组任务。
{
"tasks": {
"dev-client": "deno run --watch client/mod.ts",
"dev-server": "deno run --watch sever/mod.ts",
"dev": {
"dependencies": ["dev-client", "dev-server"]
}
}
}
在上述示例中,dev
任务用于分组 dev-client
和 dev-server
任务,但它本身没有命令。这是一种方便的方式,可以将任务组合在一起,通过一个任务名称运行。
您可以在此视频中观看 deno task
在 v2.2 演示中的更新演示
依赖管理
Deno 2.2 附带了对 deno outdated
工具的更改,增加了一种新的交互式方式来更新依赖项。
deno outdated –update –interactive
演示除了这项改进之外,还修复了多项错误,使得 deno install
和 deno outdated
更健壮、更快速。包括但不限于
- 如果运行生命周期脚本,则不重新设置
node_modules
目录 - 获取导出时使用锁定版本的 jsr 包
- 如果路径是 npm 包和相对文件,则不报错
- 从指定的配置文件中删除 importMap 字段
- 警告未在
deno install --global
中包含自动发现的配置文件 - 允许在
deno outdated
中使用--latest
标志,而不带--update
标志 deno outdated
确保“最新”版本高于“更新”版本deno outdated
在没有配置文件时报错deno outdated
更新时保留严格的 semver 规范deno outdated
显示更新建议deno outdated
现在支持更新外部导入映射中的依赖项deno oudated
即使在与当前版本相同的情况下也使用latest
标签
node:sqlite
支持 此版本为 Deno 带来了备受期待的 node:sqlite
模块,使内存或本地数据库操作变得简单。
import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync("test.db");
db.exec(`
CREATE TABLE IF NOT EXISTS people (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
age INTEGER
);`);
const query = db.prepare(`INSERT INTO people (name, age) VALUES (?, ?);`);
query.run("Bob", 40);
const rows = db.prepare("SELECT id, name, age FROM people").all();
console.log("People:");
for (const row of rows) {
console.log(row);
}
db.close();
$ deno run --allow-read --allow-write db.ts
People:
[Object: null prototype] { id: 1, name: "Bob", age: 40 }
请查阅我们文档中的示例以及完整的 API 参考。
Deno.cwd()
的权限检查放宽
Deno 2.2 在使用 Deno.cwd()
API 时,不再需要完整的 --allow-read
权限。
console.log(Deno.cwd());
$ deno main.js
┏ ⚠️ Deno requests read access to <CWD>.
┠─ Requested by `Deno.cwd()` API.
┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.
┠─ Learn more at: https://docs.deno.org.cn/go/--allow-read
┠─ Run again with --allow-read to bypass this prompt.
┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) > y
/dev
$ deno main.js
/dev
在此更改之前,已经可以在没有权限的情况下获取 CWD 路径,例如通过创建错误并检查其堆栈跟踪。
这项更改最初计划在 Deno 2.0 中发布,但未能赶上。我们很高兴在 v2.2 中迎来它。
deno compile
更小、更快的 deno compile
的多项性能和用户体验改进
- 在 macOS 上,程序现在大约小了 5MB
- 读取编译程序中嵌入的文件比 Deno 2.1 快约 40%
deno compile
现在会显示包含文件的摘要及其大小(包括node_modules
目录)
npm:cowsay
进行 deno compile
的摘要输出deno bench
更精确的 deno bench
是一个内置工具,可让您快速轻松地对代码进行基准测试。Deno v1.21 更改了 deno bench
的行为,以自动执行基准测试的预热,并自动决定执行多少次迭代,当后续运行之间的时间差异在统计上不显著时停止。
在大多数情况下,这工作得很好,但有时,您希望对执行多少次预热运行和测量运行进行精细控制。为此,Deno v2.2 带回了 Deno.BenchDefinition.n
和 Deno.BenchDefinition.warmup
选项。指定它们将使 deno bench
执行所需次数的运行
Deno.bench({ warmup: 1_000, n: 100_000 }, () => {
new URL("./foo.js", import.meta.url);
});
上述基准测试将精确执行 1000 次“预热运行”——这些运行不进行测量,仅用于“预热”V8 引擎的 JIT 编译器。之后,基准测试将执行 100,000 次测量运行,并根据这些迭代显示指标。
WebTransport
和 QUIC API
Deno 2.2 附带了对 `WebTransport` API 的实验性支持以及新的、不稳定的 Deno.connectQuic
和 Deno.QuicEndpoint
API。如果您不熟悉,QUIC(快速 UDP 互联网连接)是一种旨在取代 TCP+TLS 的现代传输协议,也是 HTTP/3 的基础。
由于这些是实验性的,它们的 API 在未来可能会发生变化,因此需要使用
--unstable-net
标志。
让我们看看这些 API 的实际应用。这是一个 QUIC 回显服务器和 WebTransport
客户端的示例。
请注意,WebTransport
需要使用 HTTPS。这些示例使用证书/密钥对;您可以使用 OpenSSL 生成自签名证书:openssl req -x509 -newkey rsa:4096 -keyout my_key.pem -out my_cert.pem -days 365
const cert = Deno.readTextFileSync("my_cert.crt");
const key = Deno.readTextFileSync("my_cert.key");
const server = new Deno.QuicEndpoint({
hostname: "localhost",
port: 8000,
});
const listener = server.listen({
cert,
key,
alpnProtocols: ["h3"],
});
// Run server loop
for await (const conn of listener) {
const wt = await Deno.upgradeWebTransport(conn);
handleWebTransport(wt);
}
async function handleWebTransport(wt) {
await wt.ready;
(async () => {
for await (const bidi of wt.incomingBidirectionalStreams) {
bidi.readable.pipeTo(bidi.writable).catch(() => {});
}
})();
(async () => {
for await (const stream of wt.incomingUnidirectionalStreams) {
const out = await wt.createUnidirectionalStream();
stream.pipeTo(out).catch(() => {});
}
})();
wt.datagrams.readable.pipeTo(wt.datagrams.writable);
}
import { decodeBase64 } from "jsr:@std/encoding/base64";
import { assertEquals } from "jsr:@std/assert";
const cert = Deno.readTextFileSync("my_cert.crt");
const certHash = await crypto.subtle.digest(
"SHA-256",
decodeBase64(cert.split("\n").slice(1, -2).join("")),
);
const client = new WebTransport(
`https://localhost:8000/path`,
{
serverCertificateHashes: [{
algorithm: "sha-256",
value: certHash,
}],
},
);
await client.ready;
const bi = await client.createBidirectionalStream();
{
const writer = bi.writable.getWriter();
await writer.write(new Uint8Array([1, 0, 1, 0]));
writer.releaseLock();
const reader = bi.readable.getReader();
assertEquals(await reader.read(), {
value: new Uint8Array([1, 0, 1, 0]),
done: false,
});
reader.releaseLock();
}
{
const uni = await client.createUnidirectionalStream();
const writer = uni.getWriter();
await writer.write(new Uint8Array([0, 2, 0, 2]));
writer.releaseLock();
}
{
const uni =
(await client.incomingUnidirectionalStreams.getReader().read()).value;
const reader = uni!.getReader();
assertEquals(await reader.read(), {
value: new Uint8Array([0, 2, 0, 2]),
done: false,
});
reader.releaseLock();
}
await client.datagrams.writable.getWriter().write(
new Uint8Array([3, 0, 3, 0]),
);
assertEquals(await client.datagrams.readable.getReader().read(), {
value: new Uint8Array([3, 0, 3, 0]),
done: false,
});
$ deno run -R --unstable-net server.js
...
$ deno run -R --unstable-net client.js
...
Node.js 和 npm 兼容性改进
Deno 2.2 一如既往地带来了大量 Node.js 和 npm 兼容性改进。以下是亮点列表
.npmrc
文件现在可在主目录和项目目录中被发现--unstable-detect-cjs
标志已被重新定位,现在是您在 Deno 中处理 CommonJS 模块时遇到问题的最终解决方案- 由于更好地处理了
HTTP 100 Continue
响应,AWS SDK 现在更可靠 tls.connect
socket 升级更可靠
process
更改
fs
更改
fs.readFile(Sync)
接受文件描述符FileHandle.chmod
现已可用FileHandle.stat
现已可用FileHandle.truncate
现已可用FileHandle.writev
现已可用FileHandle.chown
现已可用FileHandle.sync
现已可用FileHandle.utimes
现已可用- 修复 Windows 上
fs.access
/fs.promises.access
使用X_OK
模式参数的问题 - 添加
fs.stat
中缺少的path
参数验证 - 添加
fs.readFile
缺少的错误上下文 - 支持
fs.readdir
中的recursive
选项
http
模块更改
- 修复
npm:playwright
HTTP 客户端 - 改进
npm:mqtt
兼容性 - 将 socket 错误传播到客户端请求对象
- 支持
request()
中的createConnection
选项 - 支持 代理 http 请求
node:http
在ServerResponse.hasHeader()
方法中正确比较大小写
zlib
模块更改
- Brotli API 对分块使用正确的字节偏移
- 异步
brotliDecompress
API 现在可以正常工作 - 修复
crc32
中的ReferenceError
worker_threads
模块更改
worker_threads
模块更改- 如果存在挂起的异步工作,事件循环将保持活跃
data:
URL 现在使用eval
选项正确编码
crypto
模块更改
- 添加对 aes-(128|256)-gcm 密码中任意长度 IV 的支持
- 修复使用无效 AES GCM 密钥大小时的崩溃问题
- 实现
aes-128-ctr
、aes-192-ctr
和aes-256-ctr
- 实现
crypto.hash
- 实现
X509Certificate#checkHost
getCiphers
返回支持的密码timingSafeEquals
[现在抛出] (https://github.com/denoland/deno/pull/27470) 当byteLength
不同时- 在
DechiperIv#final
上检查 GCM 认证标签 - 修复
scrypt
中的崩溃问题
v8
模块更改
其他更改
- Deno 现在会监听
TZ
环境变量的变化,并相应更新 JS API 中的时区 - 修正 CommonJS 模块动态导入 ES 模块的解析问题
- 处理带有转义字符的 CommonJS
export
- 添加对工作区成员导入的
workspace:^
和workspace:~
版本约束的支持 - 当模块缺少文件扩展名时,将模块解析为可能的 CommonJS 模块
- 在“模块未找到”错误时显示目录导入和缺少扩展名的建议
- npm 依赖项的延迟缓存,仅在需要时进行
- 更好地处理 npm 包中的 TypeScript,仅限类型检查
性能改进
性能改进是每个 Deno 版本的一部分,本次也不例外。以下是一些改进列表
- Deno 现在会在超时后清除模块分析信息,从而降低内存消耗
Deno.stat
和node:fs.stat
在 Windows 上现在快了 2.5 倍- 查找最近的
package.json
比 Deno 2.1 略快 - sha256 和 sha512 实现使用汇编,使得
@aws-sdk/client-s3
提速达 2 倍 - 通过限制 URL 和路径之间的转换,加快 Node.js 模块解析速度
node:fs.cpSync
现在比 Deno 2.1 快 2 倍,比 Node.js 20 快 3 倍
WebGPU 改进
我们的 WebGPU 实现进行了重大改造,修复了许多遇到的问题,并且还应能提高可用 API 的整体性能。
除了这些修复之外,我们的 Jupyter 集成现在能够将 GPUTexture
显示为图像,并将 GPUBuffer
显示为文本
更小的 Linux 二进制文件
得益于使用了完整的 链接时优化,我们成功地将二进制文件大小减少了近 15MB。这使得 deno
从 137MB 缩小到 122MB。
TypeScript 5.7 和 V8 13.4
Deno 2.2 升级到 TypeScript 5.7 和 V8 13.4,带来了新的语言特性和性能改进。
TypedArray 现在是泛型的
TypeScript 5.7 的一个主要变化是 Uint8Array
和其他 TypedArray 现在是针对 ArrayBufferLike
的泛型。这在使用 SharedArrayBuffer
和 ArrayBuffer
时提供了更好的类型安全性,但可能需要更新一些代码库。
// Before TypeScript 5.7
const buffer: Uint8Array = new Uint8Array(new ArrayBuffer(8));
// After TypeScript 5.7 (explicitly specifying the buffer type)
const buffer: Uint8Array<SharedArrayBuffer> = new Uint8Array(
new SharedArrayBuffer(8),
);
此更改可能会引入类型错误。如果您看到类似以下错误
error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'Uint8Array<ArrayBufferLike>'.
您可能需要将 @types/node
更新到最新版本。
有关此更改的更多信息,请参阅微软的公告和TypeScript PR。
长期支持
Deno v2.1 仍是长期支持版本,并将在未来 6 个月内定期接收错误修复、安全更新和关键性能改进。
致谢
没有社区的帮助,我们无法构建 Deno!无论是通过在我们社区的 Discord 服务器中回答问题,还是报告错误,我们都非常感谢您的支持。在此特别感谢以下人员对 Deno 2.2 的贡献:Aaron Ang, Alvaro Parker, Benjamin Swerdlow, Bhuwan Pandit, Caleb Cox, Charlie Bellini, Cornelius Krassow, Cre3per, Cyan, Dimitris Apostolou, Espen Hovlandsdal, Filip Stevanovic, Gowtham K, Hajime-san, HasanAlrimawi, Ian Bull, Je Xia, João Baptista, Kenta Moriuchi, Kitson Kelly, Masato Yoshioka, Mathias Lykkegaard Lorenzen, Mohammad Sulaiman, Muthuraj Ramalingakumar, Nikolay Karadzhov, Rajhans Jadhao, Rano, Sean McArthur, TateKennington, Tatsuya Kawano, Timothy, Trevor Manz, ZYSzys, hongmengning, ingalless, jia wei, printfn, ryu, siaeyy, ud2。
您想加入 Deno 贡献者的行列吗?在此查看我们的贡献文档,我们期待下次在列表中看到您。
信不信由你,上面列出的更改仍未涵盖 2.2 版本中所有改进。您可以在 GitHub 上查看 Deno 2.2 合并的完整拉取请求列表。
感谢您关注我们的 2.2 版本发布,希望您喜欢使用 Deno 进行构建!
在 Twitter、Discord、BlueSky 和 Mastodon 上获取技术支持、分享您的项目,或者只是打个招呼。