Skip to main content

Deno 1.19 Release Notes


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

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

deno upgrade

If you are installing Deno for the first time, you can use one of the methods listed below:

# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh

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

# Using Homebrew (macOS):
brew install deno

# Using Scoop (Windows):
scoop install deno

# Using Chocolatey (Windows):
choco install deno

New features and changes

deno vendor

Over the last couple of months we have received feedback that users would like to be able to vendor their program’s dependencies into their code repositories. This is a useful feature, if you want make sure that only some very specific code gets executed by your application.

Previously, we have recommended that users check in their DENO_DIR into the repository to do vendoring. While this generally works, it is not a great user experience. The files in DENO_DIR have non intuitive names made up of hex strings that don’t look good in version control.

In this release we are introducing an improved method to vendor dependencies: deno vendor. This subcommand can be invoked with one or more entrypoints to the modules to be vendored. Deno will then build a module graph from these files by analyzing all of the imports and exports of the modules. The resulting list of modules is then written to the vendor/ folder with names that as closely resemble the original module names as possible. Sometimes we have to strip or append some characters to the name to make the specifier a valid file system path. Inside of the vendor folder we then generate an import map that maps all of the remote modules that were vendored to the local vendor directory.

To then use the vendored dependencies in your program, you just add --import-map=vendor/import_map.json to your Deno invocations. You can also add --no-remote to your invocation to completely disable fetching of remote modules (only allow importing modules in the vendor directory).

$ deno vendor main.ts
$ tree
.
├── main.ts
└── vendor
    ├── deno.land
    ├── import_map.json
    └── raw.githubusercontent.com
$ deno run --no-remote --import-map=vendor/import_map.json main.ts

The vendor directory should be checked into version control. The file names stay as consistent as possible across runs of deno vendor to minimize bloated git diffs.

One other great use case for this feature is to allow you to temporarily add some console.log entries or similar to dependency code while debugging. To do this you can just vendor the specific module you want to modify, and then edit that source code in the vendor directory. Once you are done debugging, you can just remove the vendor directory again and continue as normal.

We’d love to hear your feedback on this feature. We are aware of a few issues that we’ll iron out over the next few weeks, but it is working well in most cases.

Permission prompt by default

One complaint is that Deno requires so many command-line flags. Often these flags are --allow-* permission allowances. Before this version, Deno would throw an exception if a permission check fails, requiring users to specify these flags each time. Now in Deno 1.19, if access is not granted, a command-line prompt will allow the user to interactively accept or deny each individual access check.

# deno run https://deno.land/std/http/file_server.ts
⚠️  ️Deno requests read access to <CWD>. Run again with --allow-read to bypass this prompt.

Allow? [y/n (y = yes allow, n = no deny)] y
⚠️  ️Deno requests net access to "0.0.0.0:4507". Run again with --allow-net to bypass this prompt.

Allow? [y/n (y = yes allow, n = no deny)] y
HTTP server listening on http://localhost:4507/

This feature has long been available by using the --prompt flag, but is now on by default. To disable the prompt, use --no-prompt. These permission prompts will only happen if you’re connected to a TTY, so one shouldn’t need to supply --no-prompt in, for example, CI scripts.

Files, network sockets, and stdio are now native web streams

The Deno.FsFile and Deno.Conn interfaces now have readable and writable properties of type ReadableStream and WritableStream respectively. This makes them integrate with other web APIs that use web streams very nicely. Some examples:

// Download a file from the web, and stream it into a file on disk.
const file = await Deno.create("./example.html");
const resp = await fetch("https://example.com");
await resp.body.pipeTo(file.writable);
// Read from stdin, and stream it to a remote server (streaming upload).
// Would be used like this: `cat file.txt | deno run --allow-net=example.com main.ts`
const resp = await fetch("https://example.com", {
  method: "POST",
  body: Deno.stdin.readable,
});
console.log("Upload succeeded?", resp.ok);

Because all of the APIs in Deno now support web streams out of the box, including our native HTTP server, composing all these APIs together is now very simple:

import { serve } from "https://deno.land/std/http/server.ts";

serve(async (req) => {
  if (req.method === "POST") {
    // Save the incoming request body to a file on disk
    const path = await Deno.makeTempFile();
    const file = await Deno.create(path);
    await req.body.pipeTo(file.writable);
    return new Response(`Saved file to ${path}`);
  } else {
    // Serve a file from disk
    const path = "./example.html";
    const file = await Deno.open(path);
    return new Response(file.readable, {
      headers: { "content-type": "text/html" },
    });
  }
});

The new readable and writable properties can also be composed with built in stream transformers like TextEncoderStream or CompressionStream. Here is an example of a program that reads from stdin, performs gzip compression, and then writes the result to stdout:

await Deno.stdin.readable
  .pipeThrough(new CompressionStream("gzip"))
  .pipeTo(Deno.stdout.writable);

CompressionStream and DecompressionStream are now supported

This release is adding two new built-in stream transformers called CompressionStream and DecompressionStream. This web standard API allows you to compress and decompress data in multiple file formats (currently gzip and deflate).

The API has already shipped in Chrome, and is hopefully coming to other browsers soon.

Here is an example of performing streaming decompressing a .gz file:

const input = await Deno.open("./file.txt.gz");
const output = await Deno.create("./file.txt");
await input.readable
  .pipeThrough(new DecompressionStream("gzip"))
  .pipeTo(output.writable);

We’re currently working with the web standards groups to add support for the brotli compression algorithm to the CompressionStream and DecompressionStream APIs. You can follow this issue for updates.

Better errors for ops and resource sanitizers in Deno.test

Since the first stable release of deno test it has had resource and op sanitizers. These sanitizers check that a test does not leak resources (like open file handles) or async ops (like timers). This is done because leaking resources or async ops can often cause hard to debug test failures or flakes, and they often point to logic errors in the code.

This release completely overhauls the error messages from these sanitizers to make them more usable, and easier to understand for new users. Take this test that leaks a Deno.FsFile resource, for example:

Deno.test("leaky", () => {
  const file = Deno.openSync("/etc/passwd");
});

It would have previously failed with this error:

running 1 test from ./test.ts
test leaky ... FAILED (4ms)

failures:

leaky
AssertionError: Test case is leaking resources.
Before: {
  "0": "stdin",
  "1": "stdout",
  "2": "stderr"
}
After: {
  "0": "stdin",
  "1": "stdout",
  "2": "stderr",
  "3": "fsFile"
}

Make sure to close all open resource handles returned from Deno APIs before
finishing test case.
    at assert (deno:runtime/js/06_util.js:41:13)
    at resourceSanitizer (deno:runtime/js/40_testing.js:153:7)
    at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:169:9)
    at async runTest (deno:runtime/js/40_testing.js:427:7)
    at async Object.runTests (deno:runtime/js/40_testing.js:540:22)

failures:

        leaky

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (11ms)

It now fails with a much more concise and useful message:

running 1 test from ./test.ts
test leaky ... FAILED (4ms)

failures:

leaky
AssertionError: Test case is leaking 1 resource:

 - A file (rid 3) was opened during the test, but not closed during the test. Close the file handle by calling `file.close()`.

    at assert (deno:runtime/js/06_util.js:46:13)
    at resourceSanitizer (deno:runtime/js/40_testing.js:313:7)
    at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:329:9)
    at async runTest (deno:runtime/js/40_testing.js:587:7)
    at async Object.runTests (deno:runtime/js/40_testing.js:700:22)

failures:

        leaky

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (10ms)

The same treatment has also been applied to async op sanitizer errors. This test leaks a sleep operation:

Deno.test("leaky", () => {
  setTimeout(() => {}, 1000);
});

Previously it would have failed with this error:

running 1 test from ./test.ts
test leaky ... FAILED (4ms)

failures:

leaky
AssertionError: Test case is leaking async ops.
Before:
  - dispatched: 0
  - completed: 0
After:
  - dispatched: 2
  - completed: 1
Ops:
  op_sleep:
    Before:
      - dispatched: 0
      - completed: 0
    After:
      - dispatched: 2
      - completed: 1

Make sure to await all promises returned from Deno APIs before
finishing test case.
    at assert (deno:runtime/js/06_util.js:41:13)
    at asyncOpSanitizer (deno:runtime/js/40_testing.js:121:7)
    at async resourceSanitizer (deno:runtime/js/40_testing.js:137:7)
    at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:169:9)
    at async runTest (deno:runtime/js/40_testing.js:427:7)
    at async Object.runTests (deno:runtime/js/40_testing.js:540:22)

failures:

        leaky

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (9ms)

The new error has a helpful hint about what causes this problem, and it even includes a stack trace of where the async operation was started so you don’t have to manually try to find it with console logs:

running 1 test from ./test.ts
test leaky ... FAILED (8ms)

failures:

leaky
Test case is leaking async ops.

- 1 async operation to sleep for a duration was started in this test, but never completed. This is often caused by not cancelling a `setTimeout` or `setInterval` call. The operation was started here:
    at Object.opAsync (deno:core/01_core.js:155:42)
    at runAfterTimeout (deno:ext/timers/01_timers.js:234:31)
    at initializeTimer (deno:ext/timers/01_timers.js:200:5)
    at setTimeout (deno:ext/timers/01_timers.js:337:12)
    at ./test.ts:2:3
    at testStepSanitizer (deno:runtime/js/40_testing.js:432:13)
    at asyncOpSanitizer (deno:runtime/js/40_testing.js:145:15)
    at resourceSanitizer (deno:runtime/js/40_testing.js:360:13)
    at Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:415:15)
    at runTest (deno:runtime/js/40_testing.js:673:18)

failures:

        leaky

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (17ms)

console.log now displays circular references in logged objects

Previously inspection of circular objects would only tell you that there is some sort of circularity, but not which objects are being referenced in it. With this change, we now provide a counter and reference indicator of the object causing a circular reference.

Example:

const x = { a: {}, b: {}, foo: { deno: "land", bar: 1 } };
x.a.x = x;
x.b.y = x;
console.log(x);
<ref *1> { a: { x: [Circular *1] }, b: { y: [Circular *1] }, foo: { deno: "land", bar: 1 } }

This doesn’t only apply to objects, but also Error.causes:

const x = new Error("foo");
const y = new Error("bar", { cause: x });
x.cause = y;
console.log(y);
<ref *1> Error: bar
    at file:///dev/denoland/dotcom/test.ts:2:11
Caused by Error: foo
    at file:///dev/denoland/dotcom/test.ts:1:11
Caused by [Circular *1]

Deno.File has been renamed to Deno.FsFile

Deno’s abstraction for file system files was previously called Deno.File. Unfortunately this name causes a lot of confusion for users because of the File Web API that is available globally both in browsers and Deno.

To avoid confusion in the future we’ve decided to deprecate Deno.File and replace it with Deno.FsFile which should better convey the message that it abstracts files on the file system. Deno.File API will be available until Deno 2.0, but we suggest you migrate existing code immediately.

This is purely a rename - all methods on the Deno.File class are still the same. Only the name changes from Deno.File to Deno.FsFile.

A new lint was added to deno lint that will help catch uses of Deno.File and suggest changes to avoid using a deprecated API.

deno compile now works more reliably

Previously deno compile would bundle your entire JS program into a single ES module during compilation. This bundling sometimes leads to the code not behaving exactly the same as prior to bundling: stack traces might be off, import.meta.url is incorrect, order of execution might differ slightly to prior to bundling.

To combat this deno compile now serializes your ES module graph into the produced binary “as is” without bundling. This means that the order of execution of the code stays correct, and things like import.meta.url stay intact.

To do this we are making use of our eszip library and file format, which allows for very fast serialization and deserialization of ES module graphs.

Thank you to @williamtetlow for contributing this feature.

Allow disabling clear screen on file watcher restarts

Deno ships with a built-in file watcher that restarts running process when files change; it can be used with multiple subcommands by passing the --watch flag. Deno automatically discovers which files should be watched, but you can additionally pass paths you want to be watched (deno run --watch=file1.ts,file2.js script.ts).

The file watcher automatically cleared your terminal screen on each restart, but users reported that in some situations this is not desirable. In this release we added the --no-clear-screen flag that can be used with the --watch flag to tell Deno not to clear the terminal screen:

deno lint --watch --no-clear-screen

Thank you to @ah-yu for contributing this feature.

deno coverage gets --output flag

This release adds --output flag to deno coverage subcommand that can be used when producing lcov report.

Before this release the report was printed to standard output and could be piped manually to a file:

deno coverage --lcov cov/ > cov.profile

Starting with this release you can instead use --output flag to write to a file directly:

deno coverage --lcov --output=cov.profile cov/

Thank you to @VishnuJin for contributing this feature.

Stabilization of signal listener APIs

We have stabilized the signal listener APIs in this release. These APIs can be used to intercept and handle signals like SIGINT (Ctrl-C in your shell) with custom logic. You don’t need to pass the --unstable flag to use these APIs anymore.

const listener = () => {
  console.log("Got SIGTERM!");
};

// Starts listening for SIGTERM
Deno.addSignalListener("SIGTERM", listener);

// Stops listening for SIGTERM
Deno.removeSignalListener("SIGTERM", listener);

Note: While this API is now stable, the API is not yet available on Windows. Please follow the issue #10236 for updates on this. Stabilization here means that the API surface will not be changed in future minor releases.

Serve HTTP over Unix sockets

Deno’s HTTP server API now supports connections established over Unix sockets in addition to TCP.

import { serveListener } from "https://deno.land/std/http/server.ts";

const listener = Deno.listen({ transport: "unix", path: "/path/to/socket" });

serveListener(listener, (req) => {
  return new Response("Hello World");
});

Just like with Unix sockets in general, this API is still unstable. You need to pass the --unstable flag to use it.

Thank you to @ylxdzsw for contributing this feature.

New unstable API: Deno.Conn#setNoDelay() and Deno.Conn#setKeepAlive()

Two new APIs were added to Deno.Conn:

  • Deno.Conn.setNoDelay()
  • Deno.Conn.setKeepAlive()

These APIs allow to configure use of Nagle’s algorithm and keepalive functionality for TCP connections.

Example:

const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });

// Enable Nagle's algorithm
conn.setNoDelay(false);

// Disable Nagle's algorithm
conn.setNoDelay(true);

// Enable keep-alive
conn.setKeepAlive(true);

// Disable keep-alive
conn.setKeepAlive(false);

Note: The API is still unstable. You need to pass the --unstable flag to use it.

Thank you to @yos1p for contributing this feature.

New unstable API: Deno.getUid

Deno 1.19 adds a new Deno.getUid API. Upon invocation this returns the user ID of the Deno process on Linux and macOS. You need to pass the --allow-env permission flag to use this API.

const uid = Deno.getUid();
console.log(uid); // => Prints the user ID
$ deno run --unstable --allow-env getuid.ts
501

Note: The API is still unstable. You need to pass the --unstable flag to use it.

New unstable API: Deno.networkInterfaces

This release adds a new Deno.networkInterfaces API. This API returns an array of objects containing information about the available network interfaces. You need to pass the --allow-env permission to use this API.

const interfaces = Deno.networkInterfaces();
console.log(interfaces); // => Prints network interfaces
$ deno run --unstable --allow-env network_ifs.js
[
  {
    family: "IPv4",
    name: "lo0",
    address: "127.0.0.1",
    netmask: "255.0.0.0",
    scopeid: null,
    cidr: "127.0.0.1/8",
    mac: "00:00:00:00:00:00"
  },
  ...
]

Note: The API is still unstable. You need to pass the --unstable flag to use it.

Improvements to the LSP

As always, this release brings another round of improvements to the LSP:

Code action to replace specifiers that are redirected

When importing an module from a registry like https://deno.land/x without specifying a version, the registry will automatically redirect the request to the latest version of that module. If this happens, the LSP will now offer to replace the specifier with the one that was redirected to. In combination with the warning that is shown for unversioned imports, this will make it easier for users to use versioned imports across their codebase.

Import completions now take into account import maps

Import completions now interact nicely with import maps. Specifiers specified in your import map will be offered as completions.

The hover cards that the Deno LSP offers now correctly render @link tags in JSDoc comments as clickable links to symbols. Here is an example of linking to a symbol in a deprecation message:

V8 9.9

This release of Deno upgrades V8 to 9.9, which adds some new Intl features:

Intl.Locale extensions

This release adds seven new properties to the Intl.Locale object: calendars, collations, hourCycles, numberingSystems, timeZones, textInfo, and weekInfo.

These can be used to determine valid values for these properties for a given language. For example, here are all of the calendars that the Egyptian language supports:

const arabicEgyptLocale = new Intl.Locale("ar-EG");
arabicEgyptLocale.calendars;
// ['gregory', 'coptic', 'islamic', 'islamic-civil', 'islamic-tbla']

Intl Enumeration

This release also adds a new Intl.supportedValuesOf(code) function that returns the list supported identifiers in V8 for the Intl APIs. For example to get a list of all calendars supported by the Intl APIs:

Intl.supportedValuesOf("calendar");
// ['buddhist', 'chinese', 'coptic', 'dangi', ...]

Or all supported currencies:

Intl.supportedValuesOf("currency");
// ['ADP', 'AED', 'AFA', 'AFN', 'ALK', 'ALL', 'AMD', ...]




HN Comments