Deno 2.2: OpenTelemetry, Lint 插件, node:sqlite
要升级到 Deno 2.2,请在终端中运行以下命令:
如果尚未安装 Deno,请运行以下命令之一进行安装,或在此处了解如何安装。
curl -fsSL https://deno.land/install.sh | sh
iwr https://deno.land/install.ps1 -useb | iex
Deno 2.2 中的新功能
此版本包含_大量_内容。这是一个快速概览,可帮助您深入了解最关心的内容:
- 内置 OpenTelemetry
- Linter 更新
deno check
的改进deno lsp
的改进deno task
的实用更新- 依赖管理
- 支持
node:sqlite
- 放宽
Deno.cwd()
的权限检查 - 更小、更快的
deno compile
- 更精确的
deno bench
WebTransport
和 QUIC APIs- Node.js 和 npm 兼容性改进
- 性能改进
- WebGPU 的改进
- 更小的 Linux 二进制文件
- TypeScript 5.7 和 V8 13.4
- 长期支持
内置 OpenTelemetry 集成
Deno 2.2 包含内置的 OpenTelemetry,用于监控日志、指标和追踪。
Deno 自动检测 console.log
、Deno.serve
和 fetch
等 API。您还可以使用 npm:@opentelemetry/api
检测您自己的代码。
让我们看一下 Deno.serve
API 的一些日志和追踪:
server.ts
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
包创建自己的指标和追踪 span。要了解更多信息,请访问 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
,则报告错误:
deno.json
{
"lint": {
"plugins": ["./my-plugin.ts"]
}
}
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`", // 使用比 `foo` 更具描述性的名称
});
}
},
};
},
},
},
} satisfies Deno.lint.Plugin;
main.js
const foo = "foo";
console.log(foo);
$ deno lint main.js
error[my-lint-plugin/my-lint-rule]: Use more descriptive name than `foo` // 使用比 `foo` 更具描述性的名称
--> /dev/main.js:1:7
|
1 | const foo = "foo";
| ^^^^^^^^^^^
Found 1 problem
Checked 1 file
除了基于访问者的 API 之外,您还可以使用类似 CSS 的选择器来定位特定节点。让我们使用选择器语法重写上面的规则。
my-plugin.ts
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`", // 使用比 `foo` 更具描述性的名称
});
},
};
},
},
},
} satisfies Deno.lint.Plugin;
Lint 插件可以使用 TypeScript 编写,Deno 在 Deno.lint
命名空间下提供了开箱即用的完整类型声明。
您可以使用本地 lint 插件,以及来自 npm 和 JSR 的插件:
deno.json
{
"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 check
,Deno 的类型检查工具,在此版本中获得了两项重大改进:
- 现在支持 JSDoc 标签
- 现在可以为每个工作区成员配置
compilerOptions
的设置
让我们详细了解一下每个方面:
现在支持 JSDoc @import
标签
类型检查时,现在支持 @import
JSDoc 标签。这使您可以内联定义导入,从而改进 JavaScript 文件中的类型检查。
add.ts
export function add(a: number, b: number): number {
return a + b;
}
main.js
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
设置。
deno.json
{
"workspace": [
"./server",
"./client"
],
"compilerOptions": {
"checkJs": true
}
}
client/deno.json
{
"compilerOptions": {
"lib": ["dom", "esnext"]
}
}
client/main.js
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 用户提供更好的 DX - 正确 识别环境模块导入
- 现在支持通配符模块增强(例如
.*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
与任务名称中的通配符一起使用,如下所示:
deno.json
{
"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 中变得流行。现在,您可以通过定义没有命令的任务来 更轻松地对任务进行分组。
deno.json
{
"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
任务进行分组,但它本身没有命令。这是一种方便的方式,可以将任务分组在一起,以便从单个任务名称运行。
您可以在 v2.2 演示视频的此视频中观看 deno task
更新的演示
依赖管理
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
确保 “Latest” 版本大于 “Update” 版本- 当没有配置文件时,
deno outdated
报错 deno outdated
在更新时保留严格的 semver 说明符deno outdated
显示更新建议deno outdated
现在支持更新外部导入映射中的依赖项deno outdated
即使在 “latest” 标签与当前版本相同时也使用它
支持 node:sqlite
此版本为 Deno 带来了备受期待的 node:sqlite
模块,使其可以轻松地处理内存中或本地数据库:
db.ts
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
权限的要求。
main.js
console.log(Deno.cwd());
Deno v2.1
$ 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.com/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 v2.2
$ 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
执行请求的运行次数:
url_bench.ts
Deno.bench({ warmup: 1_000, n: 100_000 }, () => {
new URL("./foo.js", import.meta.url);
});
上面的基准测试将精确执行 1000 次“预热运行” - 这些运行不被测量,仅用于“预热” V8 引擎的 JIT 编译器。然后,基准测试将进行 100,000 次测量运行,并显示基于这些迭代的指标。
WebTransport
和 QUIC APIs
Deno 2.2 附带了 WebTransport
API 的 实验性支持 以及新的、不稳定的 Deno.connectQuic
和 Deno.QuicEndpoint
APIs。如果您不熟悉,QUIC (Quick UDP Internet Connections) 是一种旨在取代 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
server.js
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"],
});
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);
}
client.js
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
套接字升级更加可靠
process
更改:
fs
更改:
fs.readFile(Sync) 接受文件描述符
FileHandle.chmod
现在可用FileHandle.stat
现在可用FileHandle.truncate
现在可用FileHandle.writev
现在可用FileHandle.chown
现在可用FileHandle.sync
现在可用FileHandle.utimes
现在可用- 修复 Windows 上带有
X_OK
模式参数的fs.access
/fs.promises.access
- 在
fs.stat
中添加缺失的path
参数验证 - 为
fs.readFile
添加缺失的 错误上下文 - 支持
fs.readdir
中的recursive
选项
http
模块更改:
- 修复
npm:playwright
HTTP 客户端 - 改进
npm:mqtt
兼容性 - 将 套接字错误传播到客户端请求对象
- 支持
request()
中的createConnection
选项 - 支持 代理 http 请求
node:http
正确比较ServerResponse.hasHeader()
方法中的大小写
zlib
模块更改:
worker_threads
模块更改:
worker_threads
模块更改:- 如果存在待处理的异步工作,事件循环将保持活动状态
data:
URLs 现在使用eval
选项正确编码
crypto
模块更改:
- 在 aes-(128|256)-gcm 密码中添加对任意长度 IV 的支持
- 修复使用无效 AES GCM 密钥大小时的 panic
- 实现
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
中的 panic
v8
模块更改:
其他更改:
- Deno 现在监视
TZ
环境变量的变化,并相应地更新 JS API 中的时区 - 正确解析 从 CommonJS 模块动态导入 ES 模块
- 处理带有转义字符的 CommonJS
export
s - 添加对工作区成员导入的
workspace:^
和workspace:~
版本约束的支持 - 当模块缺少文件扩展名时,将模块解析为可能的 CommonJS 模块
- 在 “module not found” 错误中显示目录导入和缺少扩展名建议
- 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,带来了新的语言功能和性能改进。
TypedArrays 现在是泛型的
TypeScript 5.7 的一项重大更改是 Uint8Array
和其他 TypedArrays 现在是 ArrayBufferLike
的泛型。这允许在使用 SharedArrayBuffer
和 ArrayBuffer
时获得更好的类型安全性,但这可能需要更新某些代码库。
const buffer: Uint8Array = new Uint8Array(new ArrayBuffer(8));
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
更新到最新版本。
在 Microsoft 的公告 和 TypeScript PR 中阅读有关此更改的更多信息。
长期支持
Deno v2.1 仍然是长期支持版本,并且将在未来 6 个月内定期接收错误修复、安全更新和关键性能改进。