Skip to main content
Deno 2 is finally here 🎉️
Learn more
Deno 1.40

We are excited to announce the release of Deno 1.40, a significant step forward in the evolution of Deno. This new version is packed with features that enhance the Deno experience, introducing the powerful Temporal API for advanced date and time operations, and embracing the latest decorator syntax for more expressive code. Alongside these advancements, we’ve implemented a series of deprecations, stabilizations, and removals aimed at streamlining Deno’s capabilities and preparing for Deno 2.

If you already have Deno installed, upgrade to version 1.40 in your terminal with:

deno upgrade

If you don’t yet have Deno installed, you can install it with one of the following commands, or many other ways.

MacOS / Linux Install

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

Windows Install

irm https://deno.land/install.ps1 | iex

Here’s an overview of what’s new in Deno 1.40:

Temporal API

The Temporal API is designed to address some of the shortcomings and complexities associated with the existing Date object in JavaScript.

Temporal proposal is actively implemented by all major JavaScript engines and we’re happy to announce it’s now available in Deno with the --unstable-temporal flag.

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

console.log("day of week", birthdayIn2030.dayOfWeek);
// day of week 7
Temporal’s user friendly API makes it easy to parse and manipulate date and time.

It’s unlikely that Temporal API will be changed and we aim to stabilize it in Deno 2. We encourage you to explore the Temporal API docs.

import.meta.filename and import.meta.dirname

Deno now supports import.meta.filename and import.meta.dirname properties.

These properties mirror __filename and __dirname properties from the CommonJS module system:

  • import.meta.filename provides the absolute path to the current module file
  • import.meta.dirname provides the absolute path to the directory containing the current module file

Both of these properties are aware of the OS specific path separator and provide proper separators for the current platform:

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

In Unix:

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

And in Windows:

$ deno run C:\dev\my_module.ts
C:\dev\my_module.ts
C:\dev\

These properties are available only for local modules (ie. modules loaded from the file system) and are undefined for remote modules (modules imported from http:// or https://).

Decorators

Deno now supports the TC39 stage 3 Decorators proposal, which will soon be implemented in all browsers.

Decorators are a proposal for extending JavaScript classes which is widely adopted among developers in transpiler environments, with broad interest in standardization. TC39 has been iterating on decorators proposals for over five years. This document describes a new proposal for decorators based on elements from all past proposals.

This feature is available in .ts, .jsx and .tsx files. Support in pure JavaScript is waiting on implementation in V8.

Here is an example of an @trace decorator that logs out whenever a function is called, and when it returns:

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();

If you rely on the legacy “experimental TypeScript decorators”, you can still use them with the following configuration:

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

Note: There was a bug discovered after v1.40.0 was cut that TypeScript’s experimentalDecorators was still turned on in the LSP. Please upgrade to v1.40.1.

Simpler imports in deno.json

The imports field in deno.json now supports a simpler syntax for specifying dependencies that have subpath exports. Previously, when wanting to use preact from npm, you had to add this to your imports object in your deno.json:

{
  "imports": {
    "preact": "npm:[email protected]",
    "preact/": "npm:/[email protected]/"
  }
}

This allowed you to import both preact, and subpath exports, like preact/hooks.

In this release we have simplified this, so that you can now just do:

{
  "imports": {
    "preact": "npm:[email protected]"
  }
}

In Deno 1.40.0, this will allow you to import both preact and preact/hooks from npm.

Previously Deno considered the imports field in deno.json to just be a regular import map. Now, we pre-process the imports field in deno.json and expand any entries with a npm: prefix on the right hand side into two entries in an internal import map that we use for resolution.

Deprecations, stabilizations, and removals

As we gear up for Deno 2, we’re committed to refining the runtime while ensuring a smooth transition from Deno 1. While most Deno 1 code will remain compatible, we’re streamlining certain APIs for the platform’s long-term health.

Deprecations

We’re introducing deprecations to phase out older APIs, alongside warnings to assist your migration:

  • window – The global variable window is used often across the JavaScript ecosystem to test if code is running in a browser. It is an easy fix to use globalThis or self instead. There is no runtime warning for this deprecation yet, but it will show up in deno lint in starting this release.

  • Deno.run() – This is the old and error-prone subprocess API. We have since introduced the much more versatile Deno.Command API which was stabilized a year ago.

  • Deno.serveHttp() – Replaced by the faster and simpler Deno.serve(). Documentation here.

  • Deno.metrics() – To focus on performance, we’re shifting towards more targeted command-line flags and APIs. Custom metrics implementations are available via op_metrics_factory_fn and a new --strace-ops flag for runtime ops tracing.

  • Stream-related Functions: Transitioning to Web Streams, we’re deprecating several Deno.Reader/ Deno.Writer stream functions. The deprecated functions are still accessible via the Deno standard library:

  • Deno.FsWatcher.return() – To align with other async iterators, we’re deprecating this in favor of explicit close methods.

  • Deno.customInspect – To encourage browser-compatible code, we’ve switched to Symbol.for("Deno.customInspect").

In Deno 2, we are removing the concept of “resource ids”. Resource ids are integer references to sockets, files, or other resources managed outside of JavaScript. They’re analogous to file descriptors. However most users do not touch these directly, and we’d like to start introducing resources reference by native JavaScript objects. For this reason we’re deprecating these APIs:

  • Deno.isatty() – instead use the isTerminal() method, for example Deno.stdout.isTerminal().

  • Deno.close() – this API also operates on rid. Users are encouraged to use .close() method on relevant objects instead. For example, file.close() rather than Deno.close(file.rid).

  • Deno.resources() – This API also exposes resource ids and has little utility.

  • Deno.ftruncate() and Deno.ftruncateSync() – These function are now avaiable on Deno.FsFile as truncate() and truncateSync().

All rid properties are now deprecated and will be removed in Deno 2.0:

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

Loading certificates from files is now deprecated, read them yourself instead:

  • Deno.ListenTlsOptions.certFile - Use Deno.ListenTlsOptions.cert and Deno.readTextFile instead.
  • Deno.ListenTlsOptions.keyFile - Use Deno.ListenTlsOptions.key and Deno.readTextFile instead.
  • Deno.ConnectTlsOptions.certFile -Use Deno.ConnectTlsOptions.cert and Deno.readTextFile instead.

Stabilizations

Following Deno APIs have been stabilized and un-flagged:

  • Deno.Conn.ref()
  • Deno.Conn.unref()
  • Deno.connect() for unix transport
  • Deno.connectTls
  • Deno.stderr.isTerminal()
  • Deno.stdin.isTerminal()
  • Deno.stdout.isTerminal()
  • Deno.TlsConn.handshake()

Removals

Finally, the unstable API Deno.upgradeHttp has been removed. This API was error prone and easy to misuse. We encourage everyone to use Deno.serve() and Deno.upgradeWebsocket().

Web API: rejectionhandled event

We’ve added support for the rejectionhandled event, which is emitted anytime a .catch() handler is attached to a promise that has already been rejected. Also, this event will only emitted if you have a unhandledrejection event listener that calls event.preventDefault() (otherwise the promise will be rejected and the process will exit with an error).

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 windowing / “Bring your own Window”

We’re introducing a new unstable Deno.UnsafeWindowSurface API to address windowing in Deno. Our goal is to provide a windowing solution for WebGPU without linking to native windowing systems like X11.

This is a low level API that can be used by FFI windowing libraries like sdl2, glfw, raylib, winit and more to create a WebGPU surface using native window and display handles.

Here’s an example using deno.land/x/sdl2:

import {
  EventType,
  WindowBuilder,
} from "https://deno.land/x/[email protected]/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();
}

Running with deno run --allow-ffi --unstable-webgpu --unstable-ffi:

Here’s a demo music player app built using this API: https://github.com/littledivy/wgui

For more details on low level use, check out the PR: https://github.com/denoland/deno/pull/21835

Read more about GPUCanvasContext here.

Node.js API updates

The following built-in Node APIs are now available:

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

Additionally we fixed several bugs in already supported Node.js APIs:

  • child_process.ChildProcess.send() on Windows
  • crypto.createDeciperiv() now supports aes-192-ecb and aes-256-ecb
  • fs.promises.readFile()
  • http.ClientRequest.socket.remoteAddress
  • http.ClientRequest.socket.remotePort
  • querystring.stringify()
  • test module supports nested tests
  • zlib.brotliCompress()
  • zlib.brotliCompressSync()
  • zlib.gzipSync()
  • zlib.unzipSync()

LSP improvements

Since v1.39.0, we’ve strengthened integration with our embedded instance of TypeScript’s Language Service API to achieve significant performance boosts and some bug fixes. Data is exchanged between Rust and the TypeScript isolate more efficiently and less frequently, owing to smarter project-state synchronization and caching.

Quality-of-life improvement for users of the jsxImportSource compiler option: The specified remote resource will be cached automatically when the containing deno.json file is saved. This was necessary because unlike an uncached import, the diagnostics resulting from this missing resource were vague and did not point to the cause of the problem (neither from the perspective of the user nor the language server’s quick-fix generator).

Auto-import completions will work more consistently. Some cases where the completion resolution would silently error are fixed, due to better state preservation in the TypeScript isolate. Import-mapped NPM specifiers with subpaths are correctly substituted with the expected alias.

Better looking diagnostics

There is a new diagnostic printer in for deno lint and deno doc. We’ll be expanding this to other subcommands in the coming release.

deno lint updates

Three new rules are available in deno lint:

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

The no-window rule is enabled by default, while the other two are opt-in and you need to enable them in your configuration file:

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

Maintaining code quality is paramount to the success of your project and we all found ourselves in a situation where we need to suppress a warning from a linter.

In most instances we just ignore a warning and move on, but this approach doesn’t help out team members or future self in understanding why a particular warning was suppressed.

deno lint now supports additional explanation in // deno-lint-ignore and // deno-lint-ignore-file directives:

// 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() {}

Changes to how we handle unstable features

We’re evolving our approach to managing unstable features. The --unstable flag, while useful, has been somewhat imprecise, activating all unstable features simultaneously. In Deno 1.38, we introduced more granular flags to give you finer control over specific unstable features, for example --unstable-webgpu enables the new WebGPU API. Building on this, Deno 1.40 marks the beginning of the deprecation phase for the broad --unstable flag, setting the stage for its removal in Deno 2.

Additionally, we’ve improved type checking with respect to unstable APIs in deno check and the LSP. Now, type definitions for both stable and unstable APIs are automatically included during type checking. This eliminates the need to specify --unstable for accessing unstable API type definitions. However, remember to enable the specific unstable feature flags when running your program. Omitting these flags will still lead to errors, ensuring you’re aware of the unstable features in use.

This change streamlines development, offering more clarity and control over the use of Deno’s feature set.

Thank you to our contributors!

We couldn’t build Deno without the help of our community! Whether by answering questions in our community Discord server or reporting bugs, we are incredibly grateful for your support. In particular, we’d like to thank the following people for their contributions to Deno 1.40: Anwesh, Dean Srebnik, Joel Walker, Jovi De Croock, Julien Cayzac, Jérôme Benoit, Kenta Moriuchi, Kitson Kelly, Lino Le Van, Raashid Anwar, cions, king8fisher, nokazn, 林炳权.

Would you like to join the ranks of Deno contributors? Check out our contribution docs here, and we’ll see you on the list next time.

Believe it or not, the changes listed above still don’t tell you everything that got better in 1.40. You can view the full list of pull requests merged in Deno 1.40 on GitHub here.

Thank you for catching up with our 1.40 release, and we hope you love building with Deno!

🍋 Fresh 1.6 is out.

Fresh v1.6 apps have expanded plugin APIs, faster route matching, and official Tailwind CSS support.