Skip to main content
Deno 1.31 Deno 1.31 adds support for package.json, stabilizes Deno.Command API, Node-API and more...

Deno 1.31: package.json support

Deno 1.31 has been tagged and released with the following new features and changes:


If you already have Deno installed, you can upgrade to 1.31 by running:

deno upgrade

If you are installing Deno for the first time:

# MacOS and Linux
curl -fsSL https://deno.land/x/install/install.sh | sh

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

Click here for more installation options.

package.json support

This release brings an exciting new feature - package.json support. Deno is now even more accessible, enabling developers to transition from existing Node projects with ease.

Deno will now automatically detect a package.json and use it to install and resolve the dependencies used (ex. import express from "express"). Additionally, this enables the ability to run project specific scripts defined in the scripts section with deno task.

Note: currently support for “scripts” is limited to simple scripts. Programs like rimraf or cross-env will not work; we will add support for such programs in an upcoming release.

For an example, say we have the following package.json:

{
  "name": "@deno/my-example-app",
  "description": "An example app created with Deno",
  "type": "module",
  "scripts": {
    "say-hello": "cowsay 'Hello from deno!'"
  },
  "dependencies": {
    "chalk": "^5.2"
  },
  "devDependencies": {
    "cowsay": "^1.5"
  }
}

With the following script that references chalk via a bare specifier:

// main.ts
import chalk from "chalk";
console.log(chalk.green("Hello from Deno!"));

We can run this script:

> deno run --allow-env --allow-sys main.ts
Hello from Deno!

Or execute package.json scripts via deno task:

> deno task say-hello
Task say-hello cowsay 'Hello from deno!'
 __________________
< Hello from deno! >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

For a more complex example, you can try it yourself by running an example Vite project:

$ deno run -A npm:create-vite vite-project --template vue
$ cd vite-project
$ deno task dev

It is important to note that Deno still supports web-standard import maps, which remain the preferred way to map bare specifiers in Deno.

As always, Deno is committed to making development as smooth and efficient as possible. We are thrilled by the possibilities this feature brings and eagerly await feedback from the community.

Stabilization of Node-API

Deno 1.31 stabilizes the Node-API (aka N-API), which means that the --unstable flag is no longer needed when using npm packages that rely on Node-API.

Additionally many bug fixes were applied to Node-API and many more packages can now be used without issue. We plan to address remaining bugs in the coming weeks.

Compatibility layer is now part of the runtime

Deno 1.31 brings a significant improvement to the runtime by moving the compatibility layer for Node.js directly into the Deno runtime itself. In previous releases, the compatibility for Node.js was provided using https://deno.land/std/node - a collection of polyfills implemented in userland code in Deno’s standard library. With each Deno release, the dependency on https://deno.land/std had to be bumped, which resulted in having to download the compatibility layer after each upgrade. Moreover, this layer is sizeable (around 1.5Mb), leading to further slowdowns on startup when repeatedly executing this code.

To address these issues, the Deno team has taken radical steps to improve the situation for users relying on npm packages - either via npm: specifiers or on the newly added package.json auto-discovery. The whole compatibility layer is now embedded in the Deno runtime itself, and V8 snapshots are used to drastically reduce the startup time. This tighter integration enables easier polyfilling of missing APIs, and enhances the performance of already supported built-in Node.js modules.

To use the embedded Node polyfills now, you can import from node: specifiers. For example, to import Node’s fs module, import from node:fs. This change will significantly improve the performance of Deno applications and enable the Deno team to more easily support and enhance the runtime’s compatibility with Node.js.

npm specifiers are supported in remote modules

Previously importing a remote module that depended on an npm package would require the --unstable flag. This is no longer necessary.

Try it yourself:

deno run --allow-env --allow-sys https://gist.githubusercontent.com/dsherret/2273fbf9eb3c3ac73b4862cf6633c4cf/raw/442c6cea52f5a292359963a22fdab7fd7e4133ff/main.ts

Breaking change in FFI API

Pointers in Deno have been represented by a pointer number (number or BigInt) since 1.24, and a custom Deno.PointerValue type was provided for this. The type was also used as the type for 64-bit integers.

Starting in Deno 1.31 pointers are now represented as plain objects, or null for null pointers. These objects are created directly by V8 and supported by V8 Fast API, meaning that the performance of FFI when working with "pointer" type parameters and return values has further improved from previous versions. The pointer objects are opaque and cannot be manipulated from JavaScript directly. This means that FFI becomes safer to use, as pointer spoofing is no longer as easy as writing a JavaScript number.

This is a breaking change to the as-of-yet unstable FFI API. The Deno.PointerValue type still exists but now stands for null | PointerObject, and 64-bit integer parameters and return values now explicit use the type number | bigint. Migrating from the previous version thus means:

  • Replacing usage of Deno.PointerValue with number | bigint in places where it has been used to mark a 64-bit integer instead of a pointer.
  • Replacing usage of 0 as null pointers with null.
  • Replacing pointer arithmetic with Deno.UnsafePointer.offset(pointer, offset) usage.

In places where a pointer number must be turned into a pointer (eg. getting a pointer from a TypedArray), use Deno.UnsafePointer.create(address).

Changes to Deno APIs

API stabilizations

This release stabilizes two APIs: Deno.Command, and Deno.osUptime(). This means that the --unstable flag is no longer needed to use these APIs.

We recommend users migrate from the Deno.run() API to the new Deno.Command API, as we plan to deprecate Deno.run() in the future.

Extensions to stable APIs

Deno.build.os now returns more variants of operating systems: "darwin" | "linux" | "windows" | "freebsd" | "netbsd" | "aix" | "solaris" | "illumos". While some of these are not officially supported, some users are compiling Deno themselves and asked for the extended list of operating systems.

Deno.resolveDns() gets a new signal option, that allows passing an AbortSignal to the API for cancelling an in-flight operation.

const ac = new AbortController();
const promise = Deno.resolveDns("www.example.com", "A", {
  nameServer: { ipAddr: "127.0.0.1", port: 4553 },
  signal: ac.signal,
});
ac.abort();

Changes to the command line interface

deno bundle deprecation

The deno bundle command is no longer supported and will not show up in the help output. While Deno has no immediate plans to remove it, users are encouraged to switch to other bundling solutions such as deno compile, deno_emit, esbuild, rollup, or others.

It is important to note that bundling JavaScript for browsers is a complex process with various options and opinions. deno bundle was not an adequate solution for browser bundling. Its primary function was to create a self-contained file for running server-side code in Deno, which has led to confusion among users.

As Deno continues to support npm and built-in Node modules, supporting deno bundle becomes more difficult. Therefore, given that there are already excellent bundlers in userland, the decision has been made to remove this functionality from the runtime itself.

JSON reporter for deno bench

deno bench now accepts a --json flag that will print the benchmark results as JSON. This allows for programmatic consumption of the results.

Please note that the format is still unstable and might change in the future.

$ deno bench --json bench_me.js
{
  "runtime": "Deno/1.31.0 x86_64-apple-darwin",
  "cpu": "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz",
  "benches": [
    "origin": "file:///dev/bench_me.js",
    "group": null,
    "name": "Deno.UnsafePointerView#getUint32",
    "baseline": false,
    "result": {
      "ok": {
        "n": 49,
        "min": 1251.9348,
        "max": 1441.2696,
        "avg": 1308.7523755102038,
        "p75": 1324.1055,
        "p99": 1441.2696,
        "p995": 1441.2696,
        "p999": 1441.2696
      }
    }
  ]
}

Thanks to Serhiy Barhamon for implementing this feature.

Improvements to permission prompts

Starting with this release, the interactive permission prompt now accepts a new option A. This option allows granting permission for all subsequent calls to APIs using the granted domain.

Example:

$ deno repl
Deno 1.31.0
exit using ctrl+d, ctrl+c, or close()
> Deno.env.get("DENO")
┌ ⚠️  Deno requests env access to "DENO".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) > A
✅ Granted all env access.
undefined
> Deno.env.get("FOOBAR")
undefined
>

Previously this was not possible and the user would have to grant permissions for every single API call that asked for different values than previously requested. This led to having to answer multiple prompts and this change makes it more ergonomic to grant permissions for certain domains.

Thanks to Asher Gomez for implementing this feature.

deno compile works with statically analyzable dynamic imports

deno compile now understands dynamic imports that can be resolved at compile time, ie dynamic imports that take a plain string literal.

// main.ts
const { add } = await import("./add.ts");

console.log(add(1, 2));

// add.ts
export function add(a: number, b: number) {
  return a + b;
}
> deno compile main.ts
Compile file:///V:/example/main.ts
Emit example.exe
> ./example
3

Shorter arguments for deno fmt

deno fmt accepted a handful of options via CLI flags - all of them were in the form of --options-<option_name>. This release adds shorthand version of these flags, that omits the “options” part of the flag name.

Example:

# before
deno fmt --options-line-width=100 --options-use-tabs=true

# after
deno fmt --line-width=100 --use-tabs=true

The old flags are still supported, but they are not included in the help output.

Thanks to aryan02420 for implementing this feature.

Changes to the standard library

The major change in this release of the standard library is the removal of https://deno.land/std/node modules. This code was moved to the main deno repository and is now directly embedded in the Deno runtime. For users who are relying on https://deno.land/std/node we recommended to pin the version of std to the 0.177.0 version (https://deno.land/std@0.177.0/node/fs.ts) or by using the built-in Node module specifiers (import fs from "node:fs").

std/http/file_server.ts has become more efficient in Deno Deploy

std/http/file_server.ts is the file server implementation in the standard modules. It can work both in Deno and Deno Deploy thanks to the compatibility of FS APIs in both runtimes.

However because of the unavailability of the mtime property of files in Deno Deploy, the file server couldn’t return 304 responses to the request to the same resource, and the site could be slow when it serves the same large resources a number of times.

In this release, support for the ETag header in the Deno Deploy environment has been added to the file server. It now can return 304 responses correctly for requests to the same resource, and the site served by the file server is much optimized.

V8 11.0

This release upgrades to the latest release of V8 (11.0, previously 10.9).