原文:deno.com/blog/v1.40

作者:Bartek Iwaczuk、Leo Kettmeir等 2024年1月25日


咱们很快乐宣告发布 Deno 1.40,这是 Deno 进化的重要一步。这个新版别充满了增强 Deno 运用体验的功用,引入了强大的 Temporal API,用于高级日期和时刻操作,并选用了最新的 装修器语法 以完结更赋有表现力的代码。除此之外,咱们还完结了一系列的 弃用、安稳性改善和移除,旨在简化 Deno 的功用,并为 Deno 2 的到来做好准备。

假如你现已装置了 Deno,能够在终端中运用以下指令升级到 1.40 版别:

deno upgrade

假如你还没有装置 Deno,能够运用以下其中一条指令进行装置,或者 其他方式

# MacOS / Linux 装置
curl -fsSL https://deno.land/install.sh | sh

# Windows 装置
irm https://deno.land/install.ps1 | iex

以下是 Deno 1.40 的新功用概览:

  • Temporal API
  • import.meta.filenameimport.meta.dirname
  • 装修器
  • 更简略的 deno.json 中的 imports
  • 弃用、安稳性改善和移除
  • Web API: rejectionhandled 事情
  • WebGPU 窗口办理 / “自带窗口”
  • Node.js API 更新
  • LSP 改善
  • 更美观的确诊信息
  • deno lint 更新
  • 对不安稳功用处理的改变

Temporal API

Temporal API 旨在解决 JavaScript 中现有 Date 方针存在的一些缺陷和复杂性。

Temporal 提案现已被全部主要的 JavaScript 引擎活跃完结,并咱们很快乐地宣告它现在在 Deno 中经过 --unstable-temporal 标志可用。

const birthday = Temporal.PlainMonthDay.from("12-15");
const birthdayIn2030 = birthday.toPlainDate({ year: 2030 });
console.log(birthdayIn2030.toString());
console.log("day of week", birthdayIn2030.dayOfWeek);

Temporal 的用户友爱型 API 使得解析和操作日期和时刻变得简略。

Temporal API 不太可能发生更改,咱们的方针是在 Deno 2 中安稳它。咱们鼓舞你探索 Temporal API 文档。

Deno 现在支撑 import.meta.filenameimport.meta.dirname 特点。

这些特点镜像了 CommonJS 模块体系中的 __filename__dirname 特点:

  • import.meta.filename 供给当时模块文件的绝对途径
  • import.meta.dirname 供给包括当时模块文件的目录的绝对途径

这两个特点意识到了 OS 特定的途径分隔符,并为当时渠道供给适当的分隔符:

console.log(import.meta.filename);
console.log(import.meta.dirname);

在 Unix 中:

$ deno run /dev/my_module.ts
/dev/my_module.ts
/dev/

而在 Windows 中:

$ deno run C:devmy_module.ts
C:devmy_module.ts 
C:dev

这些特点仅对本地模块(即从文件体系加载的模块)可用,并对远程模块(从 http://https:// 导入的模块)为 undefined

装修器

Deno 现在支撑 TC39 阶段 3 的 装修器提案,这将很快在全部浏览器中完结。

装修器是一种用于扩展 JavaScript 类的提案,广泛在转译环境中得到选用,被广泛关注于标准化。TC39 在曩昔的五年里一直在迭代装修器提案。本文描述了根据曩昔全部提案的元素的一个新的装修器提案。

此功用在 .ts.jsx.tsx 文件中可用。纯 JavaScript 的支撑正在等待在 V8中的完结。

以下是一个 @trace 装修器的示例,该装修器在每次调用函数时记载日志,以及当它返回时:

function trace(fn: any, ctx: ClassMethodDecoratorContext) {
  return function (...args: unknown[]) {
    console.log("ENTERED", ctx.name);
    const v = fn(...args);
    console.log("EXITED", ctx.name);
    return v;
  };
}
class App {
  @trace
  static start() {
    console.log("Hello World!");
  }
}
App.start();

假如你依赖于旧的“实验性 TypeScript 装修器”,你依然能够在装备文件中运用以下装备来运用它们:

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

留意: 在 v1.40.0 版别之后发现了一个 bug,TypeScript 的 experimentalDecorators 依然在 LSP 中打开。请升级到 v1.40.1 版别。

更简略的 deno.json 中的 imports

deno.json 中的 imports 字段现在支撑更简略的语法来指定具有子途径导出的依赖项。以前,假如想要运用 npm 中的 preact,你有必要将以下内容添加到 deno.jsonimports 方针中:

{
  "imports": {
    "preact": "npm:preact@10.5.13",
    "preact/": "npm:/preact@10.5.13/"
  }
}

这允许你一起导入 preactpreact/hooks 等子途径导出。

在这个版别中,咱们简化了这一点,现在你只需:

{
  "imports": {
    "preact": "npm:preact@10.5.13"
  }
}

在 Deno 1.40.0 版别中,这将允许你从 npm 中导入 preactpreact/hooks

以前,Deno 将 deno.json 中的 imports 字段视为普通的 import map。现在,咱们在 deno.json 中的 imports 字段中预处理,并将右侧带有 npm: 前缀的条目展开为咱们用于解析的内部 import map 中的 两个 条目。

弃用、安稳性改善和移除

跟着咱们为 Deno 2 做准备,咱们致力于优化运转时,一起确保从 Deno 1 平滑过渡。虽然大多数 Deno 1 代码依然兼容,但咱们正在简化某些 API 以保证渠道的长期健康。

弃用

咱们引入了弃用来逐步淘汰旧的API,并供给正告来协助您的迁移:

  • window – 全局变量 window 在整个 JavaScript 生态体系中经常用于测验代码是否在浏览器中运转。运用 globalThisself 来替代它是一个简略的修正办法。现在尚无此弃用的运转时正告,但将在此版别的 deno lint 中显现。
  • Deno.run() – 这是旧的且简略犯错的子进程API。咱们现已引入了更灵敏的 [Deno.Command](https://deno.land/api?s=Deno.Command "Deno.Command") API,一年前现已安稳化
  • Deno.serveHttp() – 已被更快速、更简略的 Deno.serve() 替代。文档在这里
  • Deno.metrics() – 为了关注功能,咱们正在转向更有针对性的指令行标志和API。经过 op_metrics_factory_fn 和新的 --strace-ops 标志进行运转时操作跟踪,能够运用自定义的度量完结。
  • 与流相关的函数:为了过渡到 Web Streams,咱们正在弃用几个 Deno.Reader/Deno.Writer 流函数。这些已弃用的函数依然能够经过 Deno 标准库访问:
  • Deno.FsWatcher.return() – 为了与其他异步迭代器对齐,咱们正在弃用这个,推荐运用显式封闭办法。
  • Deno.customInspect – 为了鼓舞与浏览器兼容的代码,咱们已切换到 Symbol.for("Deno.customInspect")

在 Deno 2 中,咱们将移除“资源ID”的概念。资源ID是对在 JavaScript 之外办理的套接字、文件或其他资源的整数引证。它们类似于文件描述符。然而,大多数用户不直接操作这些,因而咱们期望开始引入由本机 JavaScript 方针引证的资源。因而,咱们正在弃用以下API:

  • Deno.isatty() – 运用 isTerminal() 办法来替代,例如 Deno.stdout.isTerminal()
  • Deno.close() – 此API也对 rid 进行操作。主张用户在相关方针上运用 .close() 办法,例如 file.close() 而不是 Deno.close(file.rid)
  • Deno.resources() – 此API还揭露资源ID,并且实用性很小。
  • Deno.ftruncate()Deno.ftruncateSync() – 这些函数现在在 Deno.FsFile 上作为 truncate()truncateSync() 供给。

全部 rid 特点现在都已被弃用,并将在 Deno 2.0 中删除:

  • Deno.Conn.rid
  • Deno.FsWatcher.rid
  • Deno.TcpConn.rid
  • Deno.TlsConn.rid
  • Deno.UnixConn.rid
  • Deno.Listener.rid

从文件加载证书现已弃用,请自行读取:

  • Deno.ListenTlsOptions.certFile – 运用 Deno.ListenTlsOptions.certDeno.readTextFile 替代。
  • Deno.ListenTlsOptions.keyFile – 运用 Deno.ListenTlsOptions.keyDeno.readTextFile 替代。
  • Deno.ConnectTlsOptions.certFile – 运用 Deno.ConnectTlsOptions.certDeno.readTextFile 替代。

安稳化

以下 Deno API 现已安稳化并取消符号:

  • Deno.Conn.ref()
  • Deno.Conn.unref()
  • Deno.connect() 用于 unix 传输
  • Deno.connectTls
  • Deno.stderr.isTerminal()
  • Deno.stdin.isTerminal()
  • Deno.stdout.isTerminal()
  • Deno.TlsConn.handshake()

移除

最终,不安稳的 API Deno.upgradeHttp 已被移除。这个 API 简略犯错且简略被乱用。咱们鼓舞每个人运用 Deno.serve()Deno.upgradeWebsocket()

Web API: rejectionhandled 事情

咱们添加了对 rejectionhandled事情 的支撑,该事情在现已回绝的 Promise 上附加了 .catch() 处理程序时触发。此外,只有在您有一个调用 event.preventDefault()unhandledrejection 事情监听器时,该事情才会触发(不然 Promise 将被回绝并且进程将以过错退出)。

globalThis.addEventListener("unhandledrejection", (event) => {
  // Call `preventDefault()` to prevent the process from exiting.
  event.preventDefault();
  console.log("unhandledrejection", event.reason);
});
globalThis.addEventListener("rejectionhandled", (event) => {
  console.log(
    "A .catch() handler was added to the promise after it has already rejected.",
    event.reason,
  );
});
// Create a rejected promise...
const a = Promise.reject(new Error("boom!"));
// ...then attach a `.catch()` handler to after a timeout.
setTimeout(async () => {
  a.catch(() => console.log("Added catch handler to the promise"));
}, 10);
$ deno run main.js
unhandledrejection Error: boom!
    at file:///dev/main.js:12:26
Added catch handler to the promise
A .catch() handler was added to the promise after it has already rejected. Error: boom!
    at file:///dev/main.js:12:26

WebGPU 窗口化 / “自带窗口”

咱们引入了一个新的不安稳的 Deno.UnsafeWindowSurface API 来处理 Deno 中的窗口。咱们的方针是为 WebGPU 供给一个窗口化的解决方案,而无需链接到像 X11 这样的本机窗口体系。

这是一个能够被 FFI 窗口库(例如 sdl2glfwraylibwinit 等)运用的低级API,能够运用本机窗口和显现句柄创立一个 WebGPU 表面。

这里是运用 deno.land/x/sdl2 的示例:

import {
  EventType,
  WindowBuilder,
} from "https://deno.land/x/sdl2@0.8.0/mod.ts";
const win = new WindowBuilder("Hello, World!", 800, 600).build();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
/* Returns a Deno.UnsafeWindowSurface */
const surface = win.windowSurface();
/* Returns a WebGPU GPUCanvasContext */
const context = surface.getContext("webgpu");
context.configure({/* ... */});
for (const event of win.events()) {
  if (event.type == EventType.Quit) break;
  // Sine wave
  const r = Math.sin(Date.now() / 1000) / 2   0.5;
  const g = Math.sin(Date.now() / 1000   2) / 2   0.5;
  const b = Math.sin(Date.now() / 1000   4) / 2   0.5;
  const textureView = context.getCurrentTexture().createView();
  const renderPassDescriptor = {
    colorAttachments: [
      {
        view: textureView,
        clearValue: { r, g, b, a: 1.0 },
        loadOp: "clear",
        storeOp: "store",
      },
    ],
  };
  const commandEncoder = device.createCommandEncoder();
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
  passEncoder.end();
  device.queue.submit([commandEncoder.finish()]);
  surface.present();
}

运用 deno run --allow-ffi --unstable-webgpu --unstable-ffi 运转:

Deno 1.40: Temporal API

这是运用此 API 构建的演示音乐播放器应用程序的示例:github.com/littledivy/…

有关低级用法的更多详细信息,请检查 PR:github.com/denoland/de…

阅读有关 GPUCanvasContext 的更多信息 here

Node.js API 更新

以下内置的 Node API 现在可用:

  • crypto.pseudoRandomBytes()
  • fs.contants
  • fs.cp()
  • fs.cpSync()
  • fs.promises.cp()
  • net.ClientRequest.setNoDelay()
  • net.UdpSocket.ref()net.UdpSocket.unref()
  • net.WriteStream.isTTY
  • os.cpus()
  • os.machine()
  • process.abort()
  • process.on("rejectionHandled")

此外,咱们在已支撑的 Node.js API 中修正了一些过错:

  • 在 Windows 上修正了 child_process.ChildProcess.send()
  • crypto.createDeciperiv() 现在支撑 aes-192-ecbaes-256-ecb
  • 修正了 fs.promises.readFile()
  • http.ClientRequest.socket.remoteAddress
  • http.ClientRequest.socket.remotePort
  • querystring.stringify()
  • test 模块支撑嵌套测验
  • zlib.brotliCompress()
  • zlib.brotliCompressSync()
  • zlib.gzipSync()
  • zlib.unzipSync()

LSP 改善

自从 v1.39.0 以来,咱们加强了与 TypeScript 的 Language Service API 的集成,以完结显著的功能提高和一些过错修正。因为更智能的项目状态同步和缓存,Rust 和 TypeScript 隔离之间的数据交换愈加高效和不频繁。

关于 jsxImportSource 编译选项的用户来说,这是一个生活质量的提高:保存包括 deno.json 文件时,指定的远程资源将主动缓存。这是必要的,因为与未缓存的导入不同,因为短少此缺失的资源而导致的确诊是含糊的,无法指向问题的原因(不管从用户还是言语服务器的快速修正生成器的视点)。

主动导入完结将愈加一致地工作。因为 TypeScript 隔离中状态保存得更好,一些静默过错解决的状况得以解决。带有子途径的导入映射的 NPM 标准符正确地替换为预期的别名。

更好看的确诊

deno lintdeno doc 引入了新的确诊打印机。咱们将在下一个版别中扩展到其他子指令。

Deno 1.40: Temporal API

deno lint 更新

deno lint 中有三个新规则:

  • no-console
  • no-self-compare
  • no-window

no-window 规则默许启用,而其他两个需求在装备文件中启用:

{
  "lint": {
    "rules": ["no-console", "no-self-compare"]
  }
}

保持代码质量对项目的成功至关重要,咱们都从前处于需求按捺来自 linter 的正告的状况。

在大多数状况下,咱们仅仅疏忽一个正告然后持续,但这种办法关于团队成员或未来的自己来说并不协助了解为什么要按捺特定的正告。

deno lint 现在支撑在 // deno-lint-ignore// deno-lint-ignore-file 指令中供给额外的解释:

// deno-lint-ignore-file -- This file is autogenerated, no need to lint it.
var __global$ = globalThis || (typeof window !== "undefined" ? window : self);
var cu=Object.create;var R=Object.defineProperty;var lu=Object.getOwnPropertyDescriptor;var iu=Object.getOwnPropertyNames;var nu=Object.getPrototypeOf...
// deno-lint-ignore no-empty -- I really need this fn have no body
export function noop() {}

对不安稳特性处理的改变

咱们正在改善咱们处理不安稳特性的办法。--unstable 标志虽然有用,但有点不够准确,一起激活全部不安稳特性。在 Deno 1.38 中,咱们引入了更细粒度的标志,使您更精密地控制特定的不安稳特性,例如 --unstable-webgpu 启用了新的 WebGPU API。根据此,Deno 1.40 标志着广泛的 --unstable 标志进入弃用阶段的开始,为其在 Deno 2 中的移除做好准备。

此外,咱们在 deno check 和 LSP 中改善了与不安稳 API 相关的类型检查。现在,在类型检查期间主动包括安稳和不安稳 API 的类型定义。这消除了访问不安稳 API 类型定义时有必要指定 --unstable 的需求。但是,在运转程序时记住启用特定的不安稳特性标志。省略这些标志仍将导致过错,确保您知道正在运用的不安稳特性。

这一改变简化了开发,供给了对 Deno 特性集的更明晰和更精密的控制。

感谢咱们的奉献者!

没有咱们社区的协助,咱们无法构建 Deno!不管是在咱们的社区 Discord 服务器 中回答问题还是 陈述过错,咱们对您的支撑感到非常感激。特别感谢以下人士对 Deno 1.40 的奉献:Anwesh,Dean Srebnik,Joel Walker,Jovi De Croock,Julien Cayzac,Jrme Benoit,Kenta Moriuchi,Kitson Kelly,Lino Le Van,Raashid Anwar,cions,king8fisher,nokazn,林炳权。

想成为 Deno 奉献者吗?在这里检查咱们的奉献文档,咱们期望下次在名单上见到您。

信不信由你,上面列出的改变依然不能告诉您 1.40 中的全部变得更好了什么。您能够在 GitHub 上检查 Deno 1.40 合并的全部拉取请求的完整列表

感谢您了解咱们的 1.40 版别,并期望您喜爱运用 Deno 构建!