Deno 1.16 Release Notes
Deno 1.16 has been tagged and released with the following features and changes:
fetch
now supports fetching file URLs- Support for new JSX transforms
- New unstable signal listener API
Error.cause
is now displayed in the console- Handshaking TLS connections can now be done explicitly
- Improvements to Web Streams API
Deno.startTls
stabilized- Per-test permissions are now stable
localStorage
does not require--location
anymore- Support for specifying a reason when aborting an
AbortSignal
- Deno to npm package build tool
- WebAssembly Reference types now available in stable
findLast
andfindLastIndex
array methods
If you already have Deno installed, you can upgrade to 1.16 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
fetch
now supports fetching file URLs
This release adds support for using fetch
to read files from the local file
system. This is useful for fetching files relative to the current module using
import.meta.url
, like is often done for WASM modules.
Here is an example of reading the /etc/hosts file:
const resp = await fetch("file:///etc/hosts");
const text = await resp.text();
console.log(text);
Note: the above example will not work on Windows, as the /etc/hosts file is located in a different location on Windows. Try the code below instead:
const resp = await fetch("file:///C:/Windows/System32/drivers/etc/hosts");
The --allow-read
permission is required to use fetch
to read files from the
local file system.
The contents of the file are read in chunks, so this is a great way to stream a large file via HTTP without having to load the entire file into memory:
import { serve } from "https://deno.land/std/http/server.ts";
serve((_req) => {
const resp = await fetch(new URL("./static/index.html", import.meta.url));
return new Response(resp.body, {
headers: { "content-type": "text/html; charset=utf-8" },
});
});
The behaviour of file fetches is not specified in any web specification, but our implementation is based on the implementation for reading files from disk in Firefox:
- If a file is not found, the promise returned from
fetch
rejects with aTypeError
. - If the item at the specified path is a directory, the promise returned from
fetch
rejects with aTypeError
. - No
content-length
header is set on the response. This is because the response body is a stream, so the exact length cannot be known ahead of time. - No
content-type
header is set on the response. If you want to derive a content type from the file extension, you can use themedia_types
module on https://deno.land/x.
Support for new JSX transforms
React 17 introduced a new JSX transform which makes improvements to the JSX transform API as well as allowing automatic importing of the JSX runtime library. Starting with this release, Deno supports these transforms.
It can be used two different ways. The first way is using the @jsxImportSource
pragma in a .jsx
or .tsx
file. For example, to use Preact from esm.sh:
/** @jsxImportSource https://esm.sh/preact */
export Welcome({ name }) {
return (
<div>
<h1>Welcome {name}</h1>
</div>
);
}
The other option is using a configuration file and setting the compiler options project wide:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "https://esm.sh/preact"
}
}
And then passing the --config
file on the command line (or setting the
deno.config
option in your editor).
For more details, refer to the manual section on configuring JSX.
New unstable signal listener API
This release adds a new unstable API for listening to operating system signals.
The new API is currently experimental and may change in the future. It
supersedes the existing Deno.signals
API (which was also unstable).
const listener = () => {
console.log("SIGTERM!");
};
// Starts listening to SIGTERM
Deno.addSignalListener("SIGTERM", listener);
// Stops listening to SIGTERM
Deno.removeSignalListener("SIGTERM", listener);
We are interested in community feedback. If you have any suggestions, please open an issue.
Error.cause
is now displayed in the console
Since Deno 1.13, the Error.cause
property has been supported as a way to
attach a cause to an error. This is useful for debugging errors that occur deep
inside of an application, allowing developers to wrap these errors in useful
information to help debug issues.
Starting in this release we will display the Error.cause
property in the
console when an error is thrown or logged via console.log
:
> throw new Error("main error", { cause: new TypeError("caused by this") })
Uncaught Error: main error
at <anonymous>:2:7
Caused by TypeError: caused by this
at <anonymous>:3:12
This matches the behaviour of Node.js 17.
Thanks to Kenta Moriuchi for implementing this feature.
Handshaking TLS connections can now be done explicitly
When establishing a connection over TLS before data can be read or written on the encrypted connection, a TLS handshake needs to be performed. Most users do not need to care about the specifics of handshaking, which is why we do it automatically when a user first tries to read or write data on the connection.
This release adds a handshake()
method to Deno.TlsConn
that can be called to
explicitly trigger a handshake. This method returns a promise that resolves once
the handshake is complete. If the method is called after the handshake is
already complete, the promise will resolve immediately. If the method is called
while a handshake is in progress, but not yet complete, the returned promise
will resolve once the handshake is completed.
Improvements to Web Streams API
This release introduces multiple new features from the Web Streams API:
ReadableStreamBYOBReader
is now supported. These “bring-your-own-buffer” (also known as BYOB) ReadableStream readers allow reading into a developer-supplied buffer, thus minimizing copies in comparison to the regularReadableStreamDefaultReader
. You can read more about this API on MDN.WritableStreamDefaultController.signal
is now supported. See the explainer for more information.ReadableStream.getIterator
has been removed. This method has been deprecated since Deno 1.7, and was never implemented in any browser. TheReadableStream
itself has always implemented theAsyncIterable
protocol, so use that instead when iterating over a stream.
Thanks to @crowlKats for the work on the Web Streams implementation.
Deno.startTls
stabilized
In Deno there are two ways to connect to a server via TLS: Deno.connectTls
and
Deno.startTls
. The first is used when you want open a TCP connection and then
immediately start talking TLS over it. The latter is used when you need to
create a plain text TCP connection first, exchange some data, and then start
talking TLS over it.
Deno 1.16 stabilizes the Deno.startTls
API. This makes it possible to write an
SMTP driver for stable Deno. It also allows Postgres and MySQL drivers written
for stable Deno. The https://deno.land/x/postgres driver for Deno now works
fully in Deno stable:
import { Client } from "https://deno.land/x/[email protected]/mod.ts";
const client = new Client({
user: "user",
database: "test",
hostname: "psql.example.com",
port: 5432,
tls: {
enforce: true,
caCertificates: [await Deno.readTextFile("/path/to/ca.crt")],
},
});
await client.connect();
const result = await client.queryObject("SELECT id, name FROM people");
console.log(result.rows); // [{id: 1, name: 'Carlos'}, ...]
Per-test permissions are now stable
Back in Deno 1.10 we introduced a feature that allows you
to set per test permissions. This makes it super easy to test how your program
behaves with different permissions set. This feature is now stable: it does not
require --unstable
anymore. For an example, check out the 1.10 blog post
linked above.
Keep in mind, permissions requested by a test case cannot exceed permissions
granted to the process using --allow-*
flags. If a key is omitted in
permissions object then it inherits its value from the respective --allow-*
flag.
localStorage
does not require --location
anymore
In previous versions of Deno, the localStorage
API could only be used if the
user started a Deno process with a --location
flag. This release adds an
implicit opaque key for the storage bucket used by localStorage
when you start
Deno without a --location
flag. This is derived as follows:
- When using the
--location
flag, the origin for the location is used to uniquely store the data. That means a location ofhttp://example.com/a.ts
andhttp://example.com/b.ts
andhttp://example.com:80/
would all share the same storage, buthttps://example.com/
would be different. - If there is no location specifier, but there is a
--config
configuration file specified, the absolute path to that configuration file is used. That meansdeno run --config deno.jsonc a.ts
anddeno run --config deno.jsonc b.ts
would share the same storage, butdeno run --config tsconfig.json a.ts
would be different. - If there is no configuration or location specifier, Deno uses the absolute
path to the main module to determine what storage is shared. The Deno REPL
generates a “synthetic” main module that is based off the current working
directory where
deno
is started from. This means that multiple invocations of the REPL from the same path will share the persistedlocalStorage
data.
Some examples to help clarify this behaviour:
# You can persist data, even without --location
$ cat one.js
console.log(localStorage.getItem("file"));
localStorage.setItem("file", Deno.mainModule);
$ deno run one.js
null
$ deno run one.js
file:///tmp/one.js
# The key for the storage bucket is derived from the main module, so a module
# cannot read or write data written by a program with a different main module.
$ cat two.js
console.log(localStorage.getItem("file"));
localStorage.setItem("file", Deno.mainModule);
$ deno run two.js
null
$ deno run two.js
file:///tmp/two.js
# The key is derived from the **main module** (the entrypoint), not the module
# that called `localStorage`!
$ cat three.js
import "./two.js";
$ deno run three.js
null
$ deno run three.js
file:///tmp/three.js
# If you have a config file, that is used as the key for the storage bucket.
$ deno run --config=deno.jsonc one.js
null
$ deno run --config=deno.jsonc one.js
file:///tmp/one.js
$ deno run --config=deno.jsonc two.js
file:///tmp/one.js
$ deno run --config=deno.jsonc one.js
file:///tmp/two.js
# You can use --location to specify an explicit origin though, like before. In
# this case, different main modules (entrypoints) can share a single storage
# bucket.
$ deno run --location=https://example.com one.js
null
$ deno run --location=https://example.com one.js
file:///tmp/one.js
$ deno run --location=https://example.com two.js
file:///tmp/one.js
$ deno run --location=https://example.com one.js
file:///tmp/two.js
More information is available in the manual: https://docs.deno.com/runtime/manual/runtime/web_storage_api
Support for specifying a reason when aborting an AbortSignal
WHATWG has recently specified support for specifying a reason when aborting an AbortSignal. Deno is the first platform to implement this new feature:
const abortController = new AbortController();
abortController.abort();
console.log(abortController.signal.reason); // DOMException: The signal has been aborted
const abortController = new AbortController();
const reason = new DOMException("The request timed out", "TimeoutError");
abortController.abort(reason);
console.log(abortController.signal.reason); // DOMException: The request timed out
Thanks to @crowlKats for implementing this feature.
Deno to npm package build tool
We continue to make improvements to our Node compatibility mode (--compat
) but
nothing concrete to announce in this release. However, we have released a new
system called dnt for publishing modules
written in Deno as npm packages.
By default, dnt will transform your Deno module to canonical TypeScript, type
check, build an ESM & CommonJS hybrid package with TypeScript declaration files,
and finally run your Deno test code in Node.js against the output. Once done,
you only have to npm publish
the output directory.
An example project that is already using dnt is deno_license_checker which is now published as an npm package: https://www.npmjs.com/package/@kt3k/license-checker
Try it out with:
npm install -g @kt3k/license-checker
This allows you to use Deno-first code in Node environments.
It’s still early days, but we would appreciate if you try it out, thoroughly test and inspect its output, and see what problems or challenges arise so we can prioritize and fix them.
V8 updated to version 9.7
This release ships with V8 9.7. Deno 1.15 shipped with V8 9.5, so this time you are getting two V8 versions worth of new JS goodies 😀
As usual, the V8 release also brings with it a bunch of performance improvements and bug fixes.
See V8’s release notes for more details: https://v8.dev/blog/v8-release-97.
WebAssembly Reference Types now supported
The Reference Types proposal, allows using external references from
JavaScript opaquely in WebAssembly modules. The externref
(formerly known as
anyref
) data type provides a secure way of holding a reference to a JavaScript
object and is fully integrated with V8’s garbage collector.
This feature was introduce in V8 9.6. See https://v8.dev/blog/v8-release-96.
findLast
and findLastIndex
array methods
The findLast
and findLastIndex
methods on Array
s and TypedArray
s find
elements that match a predicate from the end of an array.
For example:
[1, 2, 3, 4].findLast((el) => el % 2 === 0);
// → 4 (last even element)
For more info, see the feature explainer.